Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Terraform one module deploy null or multiple resources based on input (nested for loop)

This article is about little bit advanced structure in the terraform module, Here I using terraform functions to create a module where multiple resources will be created from given map or object input. If map or object is null (not mentioned) there shouldn't be error and it should skip making those changes or stop creating those resources based on the information provided. This is also a nested for loop example.

For example if I am creating 2 resource groups, in one resource group I want to create multiple storage accounts and in another there shouldn't be any storage account. Below is the code for the same.

Related Article: Hashicorp Terraform map and object inside module and variable example

In below module variable I defined data as map and object type. In main.tf, combining resource group and storage account info I created one local variable (There are two ways and I am using all required terraform functions to automate). If storage account information is not given, local variable will be null and accordingly it creates or skip creating resource.

This is also best example of flatten and coalesce.

#Location:  .\Modules\Resource_Group  - With multiple resource configuration
#Resource group module variable.tf file
variable "location_info" {
  description = "The name of the module demo resource group in which the resources will be created"
  type = object({
    location = string
    #otheroption = string
  })
}

variable "resource_info" {
  type = map(object({
    rg_name = string
    tags = object({
      environment = string
      Owner       = string
    })
    storage_accounts = optional(map(object({
      name                     = string
      account_tier             = string
      account_replication_type = string
    })), null)
  }))
}

#Resource group module main.tf file
resource "azurerm_resource_group" "example_rg" {
  for_each = {
    for key, rg in var.resource_info : rg.rg_name => rg
  }
  name     = each.value.rg_name
  tags     = each.value.tags
  location = var.location_info.location
}

locals {
  allSA = flatten([
    for rgkey, rg in var.resource_info : [
      for storagekey, storage in coalesce(rg.storage_accounts, {}) : {
        name                     = storage.name
        resource_group_name      = rg.rg_name
        account_tier             = storage.account_tier
        account_replication_type = storage.account_replication_type
      }
    ]
  ])
}

# locals {
#   allSA = coalesce(flatten([ 
#     for rgkey, rg in var.resource_info : try([
#       for storage in rg.storage_accounts : {
#         name                     = storage.name
#         resource_group_name      = rg.rg_name
#         account_tier             = storage.account_tier
#         account_replication_type = storage.account_replication_type
#       } #for storage
#     ], []) #for rg try block
#   ]), null) #allSA = coalesce
# } #locals

# Another simplified example of above commented out version.
# locals {
#   allSA = flatten([ 
#     for rgkey, rg in var.resource_info : try([
#       for storage in rg.storage_accounts : {
#         name                     = storage.name
#         resource_group_name      = rg.rg_name
#         account_tier             = storage.account_tier
#         account_replication_type = storage.account_replication_type
#       } #for storage
#     ], []) #for rg try block
#   ]) #allSA = coalesce
# } #locals

resource "azurerm_storage_account" "example_sa_name" {
  count = length(local.allSA)
  name                   = local.allSA[count.index].name
  resource_group_name      = local.allSA[count.index].resource_group_name
  location                 = "West US"
  account_tier             = local.allSA[count.index].account_tier
  account_replication_type = local.allSA[count.index].account_replication_type
  tags = {
    environment = "Dev"
    project     = "Terraform"
  }
  depends_on = [azurerm_resource_group.example_rg]
}

#Resource group module output.tf file
# output "rg_name" {
#   value = [for s in var.rg_info : s.name]
# }

output "location_name" {
  value = var.location_info.location
}

In the sa.tf which will use modules. I provided data in map format. First resource group has two storage account created and second resource group doesn't have any storage account mentioned.

Download this terraform bundle script here or it is also available on github.com.

#Location: .\
#Root sa.tf file
# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

module "Demo_Azure_Module_RG" {
  source = "./Modules/Resource_Group"

  location_info = {
    location = "West US"
  }

  resource_info = {
    rg1 = {
      rg_name = "demo_RG1"
      tags = {
        environment = "DemoRG1"
        Owner       = "http://vcloud-lab.com"
      } #tags
      storage_accounts = {
        sa1 = {
          name                     = "vcloudlab0001"
          account_tier             = "Standard"
          account_replication_type = "ZRS"
        } #sa1
        sa2 = {
          name                     = "vcloudlab0002"
          account_tier             = "Standard"
          account_replication_type = "ZRS"
        } #sa2
      }   #storage_accounts
    }     #rg1
    rg2 = {
      rg_name = "demo_RG2"
      tags = {
        environment = "DemoRG2"
        Owner       = "http://vcloud-lab.com"
      } #tags
    }   #rg2
  }     #resource_info
}       #module Demo_Azure_Module_RG

As per the input it will create 4 resources in the plan I have high lighted resource groups and where storage accounts will be created.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.Demo_Azure_Module_RG.azurerm_resource_group.example_rg["demo_RG1"] will be created
  + resource "azurerm_resource_group" "example_rg" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "demo_RG1"
      + tags     = {
          + "Owner"       = "http://vcloud-lab.com"
          + "environment" = "DemoRG1"
        }
    }

  # module.Demo_Azure_Module_RG.azurerm_resource_group.example_rg["demo_RG2"] will be created
  + resource "azurerm_resource_group" "example_rg" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "demo_RG2"
      + tags     = {
          + "Owner"       = "http://vcloud-lab.com"
          + "environment" = "DemoRG2"
        }
    }

  # module.Demo_Azure_Module_RG.azurerm_storage_account.example_sa_name[0] will be created
  + resource "azurerm_storage_account" "example_sa_name" {
      + access_tier                       = (known after apply)
      + account_kind                      = "StorageV2"
      + account_replication_type          = "ZRS"
      + account_tier                      = "Standard"
      + allow_nested_items_to_be_public   = true
      + cross_tenant_replication_enabled  = true
      + default_to_oauth_authentication   = false
      + enable_https_traffic_only         = true
      + id                                = (known after apply)
      + infrastructure_encryption_enabled = false
      + is_hns_enabled                    = false
      + large_file_share_enabled          = (known after apply)
      + location                          = "westus"
      + min_tls_version                   = "TLS1_2"
      + name                              = "vcloudlab0001"
      + nfsv3_enabled                     = false
      + primary_access_key                = (sensitive value)
      + primary_blob_connection_string    = (sensitive value)
      + primary_blob_endpoint             = (known after apply)
      + primary_blob_host                 = (known after apply)
      + primary_connection_string         = (sensitive value)
      + primary_dfs_endpoint              = (known after apply)
      + primary_dfs_host                  = (known after apply)
      + primary_file_endpoint             = (known after apply)
      + primary_file_host                 = (known after apply)
      + primary_location                  = (known after apply)
      + primary_queue_endpoint            = (known after apply)
      + primary_queue_host                = (known after apply)
      + primary_table_endpoint            = (known after apply)
      + primary_table_host                = (known after apply)
      + primary_web_endpoint              = (known after apply)
      + primary_web_host                  = (known after apply)
      + public_network_access_enabled     = true
      + queue_encryption_key_type         = "Service"
      + resource_group_name               = "demo_RG1"
      + secondary_access_key              = (sensitive value)
      + secondary_blob_connection_string  = (sensitive value)
      + secondary_blob_endpoint           = (known after apply)
      + secondary_blob_host               = (known after apply)
      + secondary_connection_string       = (sensitive value)
      + secondary_dfs_endpoint            = (known after apply)
      + secondary_dfs_host                = (known after apply)
      + secondary_file_endpoint           = (known after apply)
      + secondary_file_host               = (known after apply)
      + secondary_location                = (known after apply)
      + secondary_queue_endpoint          = (known after apply)
      + secondary_queue_host              = (known after apply)
      + secondary_table_endpoint          = (known after apply)
      + secondary_table_host              = (known after apply)
      + secondary_web_endpoint            = (known after apply)
      + secondary_web_host                = (known after apply)
      + sftp_enabled                      = false
      + shared_access_key_enabled         = true
      + table_encryption_key_type         = "Service"
      + tags                              = {
          + "environment" = "Dev"
          + "project"     = "Terraform"
        }
    }

  # module.Demo_Azure_Module_RG.azurerm_storage_account.example_sa_name[1] will be created
  + resource "azurerm_storage_account" "example_sa_name" {
      + access_tier                       = (known after apply)
      + account_kind                      = "StorageV2"
      + account_replication_type          = "ZRS"
      + account_tier                      = "Standard"
      + allow_nested_items_to_be_public   = true
      + cross_tenant_replication_enabled  = true
      + default_to_oauth_authentication   = false
      + enable_https_traffic_only         = true
      + id                                = (known after apply)
      + infrastructure_encryption_enabled = false
      + is_hns_enabled                    = false
      + large_file_share_enabled          = (known after apply)
      + location                          = "westus"
      + min_tls_version                   = "TLS1_2"
      + name                              = "vcloudlab0002"
      + nfsv3_enabled                     = false
      + primary_access_key                = (sensitive value)
      + primary_blob_connection_string    = (sensitive value)
      + primary_blob_endpoint             = (known after apply)
      + primary_blob_host                 = (known after apply)
      + primary_connection_string         = (sensitive value)
      + primary_dfs_endpoint              = (known after apply)
      + primary_dfs_host                  = (known after apply)
      + primary_file_endpoint             = (known after apply)
      + primary_file_host                 = (known after apply)
      + primary_location                  = (known after apply)
      + primary_queue_endpoint            = (known after apply)
      + primary_queue_host                = (known after apply)
      + primary_table_endpoint            = (known after apply)
      + primary_table_host                = (known after apply)
      + primary_web_endpoint              = (known after apply)
      + primary_web_host                  = (known after apply)
      + public_network_access_enabled     = true
      + queue_encryption_key_type         = "Service"
      + resource_group_name               = "demo_RG1"
      + secondary_access_key              = (sensitive value)
      + secondary_blob_connection_string  = (sensitive value)
      + secondary_blob_endpoint           = (known after apply)
      + secondary_blob_host               = (known after apply)
      + secondary_connection_string       = (sensitive value)
      + secondary_dfs_endpoint            = (known after apply)
      + secondary_dfs_host                = (known after apply)
      + secondary_file_endpoint           = (known after apply)
      + secondary_file_host               = (known after apply)
      + secondary_location                = (known after apply)
      + secondary_queue_endpoint          = (known after apply)
      + secondary_queue_host              = (known after apply)
      + secondary_table_endpoint          = (known after apply)
      + secondary_table_host              = (known after apply)
      + secondary_web_endpoint            = (known after apply)
      + secondary_web_host                = (known after apply)
      + sftp_enabled                      = false
      + shared_access_key_enabled         = true
      + table_encryption_key_type         = "Service"
      + tags                              = {
          + "environment" = "Dev"
          + "project"     = "Terraform"
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
 

Go Back

Comment

Blog Search

Page Views

12273540

Follow me on Blogarama