Reputation: 329
Hello Terraform Experts,
I inherited some old Terraform code for deploying resources to Azure. One of the main components that I see in most of the modules is to merge the Resource Group tags with additional tags that go on individual resources. The Resource Group tags are outputs as a map of tags. For example:
output "resource_group_tags_map" {
value = { for r in azurerm_resource_group.this : r.name => r.tags }
description = "map of rg tags."
}
and then a resource like vnets merges the RG tags with additional specific tags for the vnet given the name of the RG in a variable.
# merge Resource Group tags with Tags for VNET
# this is going to break if we change RGs
locals {
tags = merge(var.net_additional_tags, data.azurerm_resource_group.this.tags)
This works just fine if we can set the resource group in a single variable. It assumes that the resource(s) being deployed will go into one RG. However, this is not the case anymore and we somehow need to build in a way for any RG to be chosen when deploying a resource. The code below shows how the original concept works.
locals {
tags = merge(var.net_additional_tags, data.azurerm_resource_group.this.tags)
# - Virtual Network
# -
resource "azurerm_virtual_network" "this" {
for_each = var.virtual_networks
name = each.value["name"]
location = data.azurerm_resource_group.this.location
resource_group_name = var.resource_group_name
address_space = each.value["address_space"]
dns_servers = lookup(each.value, "dns_servers", null)
tags = local.tags
}
looking for help therefore to work around this. Say we create 100 vnets and each one of them goes into a different RG, we couldn't create 100 different resource group variables to capture that as it would become too cumbersome.
Here is my example with Key Vault
resource "azurerm_key_vault" "this" {
for_each = var.key_vaults
name = each.value["name"]
location = each.value["location"]
resource_group_name = each.value["resource_group_name"]
sku_name = each.value["sku_name"]
access_policy = var.access_policies
enabled_for_deployment = each.value["enabled_for_deployment"]
enabled_for_disk_encryption = each.value["enabled_for_disk_encryption"]
enabled_for_template_deployment = each.value["enabled_for_template_deployment"]
enable_rbac_authorization = each.value["enable_rbac_authorization"]
purge_protection_enabled = each.value["purge_protection_enabled"]
soft_delete_retention_days = each.value["soft_delete_retention_days"]
tags = merge(each.value["tags"], )
In the tags argument, we need to somehow merge the tags entered for this instance of Key Vault with the resource group tags that the user chose to place the key vault in. I thought of something like this, but clearly the syntax is wrong.
merge(each.value["tags"], data.azurerm_resource_group[each.key][each.value["resource_group_name"].tags)
Thanks for your input.
UPDATE:
│ Error: Invalid index
│
│ on Modules\keyvault\main.tf line 54, in resource "azurerm_key_vault" "this":
│ 54: tags = merge(each.value["tags"], data.azurerm_resource_group.this["${each.value.resource_group_name}"].tags)
│ ├────────────────
│ │ data.azurerm_resource_group.this is object with 1 attribute "keyvault1"
│ │ each.value.resource_group_name is "Terraform1"
│
│ The given key does not identify an element in this collection value.
Upvotes: 0
Views: 2675
Reputation: 329
Solution code posted below using a map and locals.
SOLUTION
Variables.tf
variable "key_vaults" {
description = "Key Vaults and their properties."
type = map(object({
name = string
location = string
resource_group_name = string
sku_name = string
tenant_id = string
enabled_for_deployment = bool
enabled_for_disk_encryption = bool
enabled_for_template_deployment = bool
enable_rbac_authorization = bool
purge_protection_enabled = bool
soft_delete_retention_days = number
tags = map(string)
}))
default = {}
}
# soft_delete_retention_days numeric value can be between 7 and 90. 90 is default
Main.tf for KeyVault module
data "azurerm_resource_group" "this" {
# read from local variable, index is resource_group_name
for_each = local.rgs_map
name = each.value.name
}
# use data azurerm_client_config to get tenant_id, not from config
data "azurerm_client_config" "current" {}
# -
# - Setup key vault
# - transform variables to locals to make sure the correct index will be used: resource group name and key vault name
locals {
rgs_map = {
for n in var.key_vaults :
n.resource_group_name => {
name = n.resource_group_name
}
}
kvs_map = {
for n in var.key_vaults :
n.name => {
name = n.name
location = n.location
resource_group_name = n.resource_group_name
sku_name = n.sku_name
tenant_id = data.azurerm_client_config.current.tenant_id # n.tenant_id
enabled_for_deployment = n.enabled_for_deployment
enabled_for_disk_encryption = n.enabled_for_disk_encryption
enabled_for_template_deployment = n.enabled_for_template_deployment
enable_rbac_authorization = n.enable_rbac_authorization
purge_protection_enabled = n.purge_protection_enabled
soft_delete_retention_days = n.soft_delete_retention_days
tags = merge(n.tags, data.azurerm_resource_group.this["${n.resource_group_name}"].tags)
}
}
}
resource "azurerm_key_vault" "this" {
for_each = local.kvs_map # use local variable, other wise keyvault1 will be used in stead of kv-eastus2-01 as index
name = each.value["name"]
location = each.value["location"]
resource_group_name = each.value["resource_group_name"]
sku_name = each.value["sku_name"]
tenant_id = each.value["tenant_id"]
enabled_for_deployment = each.value["enabled_for_deployment"]
enabled_for_disk_encryption = each.value["enabled_for_disk_encryption"]
enabled_for_template_deployment = each.value["enabled_for_template_deployment"]
enable_rbac_authorization = each.value["enable_rbac_authorization"]
purge_protection_enabled = each.value["purge_protection_enabled"]
soft_delete_retention_days = each.value["soft_delete_retention_days"]
tags = each.value["tags"]
}
Upvotes: 1