Vegas588
Vegas588

Reputation: 329

Finding Ways to Merge Resource Tags

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

Answers (1)

Vegas588
Vegas588

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

Related Questions