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. |
Create an Azure virtual machine scale set and load balancer using Terraform
Azure Terraform fixed Availibility Zones on Virtual Machine Scale Set
Writing and Using Terraform modules
Terraform Using one module variable in another module
Hashicorp Terraform dynamic block with example
Terraform for_each loop on map example
Terraform for_each loop on resource example
Terraform manage similar resources with for_each loop inside modules
Importing existing resources into Terraform - Step by Step
Importing already created module Infrastructure into Terraform and update state file
Conditionally create resources in Terraform