Looping through nested list

I am attempting to create a module that will loop through a nested list of Azure Policy Sets and add a dynamic set of policies to that policy set. Struggling to figure out how to reference the inner list of “policies”. Here is the data structure that I would like to use.


policyset_definitions = [
  {
    security = {
        name               = "security_governance"
        policy_type        = "Custom"
        display_name       = "Security Governance"
        description        = "Contains common Security Governance policies"
        policies = {
            policy_description = "Internet-facing virtual machines should be protected with network security groups"
            effect             = "AuditIfNotExists"
        },
        {
            policy_description = "Subnets should be associated with a Network Security Group"
            effect         = "AuditIfNotExists"
        }
  }
    data_protection = {
        name               = "data_governance"
        policy_type        = "Custom"
        display_name       = "Data Governance"
        description        = "Contains common Data Governance policies"
        policies = {
            policy_description = "Azure Backup should be enabled for Virtual Machines"
            effect             = "AuditIfNotExists"
        },
        {
            policy_description = "Long-term geo-redundant backup should be enabled for Azure SQL Databases"
            effect         = "AuditIfNotExists"
        }
  },
    iam = {
        name               = "iam_governance"
        policy_type        = "Custom"
        display_name       = "IAM Governance"
        description        = "Contains common IAM Governance policies"
        policies = {
            policy_description = "Audit usage of custom RBAC rules"
            effect             = "AuditIfNotExists"
        },
        {
            policy_description = "Custom subscription owner roles should not exist"
            effect         = "AuditIfNotExists"
        }
  }
]

I have tried to build a local flattened policy variable as shown below to attempt to grab the policies.


locals {
  policy_pairs = flatten([
    for pol_set in var.policyset_definitions : [
      for pol in pol_set.policies : [
        for desc in pol.policy_description : {
          name               = pol_set.name
          policy_type        = pol_set.policy_type
          display_name       = pol_set.display_name
          policy_description = desc
          effect             = pol.effect
  }]]])

  policy_map = {
  for i in local.policy_pairs : i.policy_description => i.effect }
}

output "map" {
  value = local.policy_map
}
> Blockquote

> Blockquote
resource "azurerm_policy_set_definition" "prod_policy_set" {

  #for_each = var.policyset_definitions
  for_each = local.policy_map

  name         = each.value.name
  policy_type  = each.value.policy_type
  display_name = each.value.display_name
  description  = each.value.description

  metadata = <<METADATA
    {
    "category": "${var.policyset_definition_category}"
    }
METADATA

  dynamic "policy_definition_reference" {
    for_each = [for item in var.policyset_definitions : {}
    ]

    content {
      policy_definition_id = item.name
      parameters = {
        Effect = item.effect
      }
    }
  }
}

When I print out my local map, it is EMPTY (no output). What I am expecting to get is a new “azurerm_policy_set_definition” resource created for each top-level value in my “policyset_definitions” variable [security, data_protection, iam]. Each of those 3 policy_set_definitions should have 2 policies added, as defined in the same “policyset_definitions” variable. The policies are listed under the variable “policies”.

Am I approaching this the right way? Any help structuring the code to process the data structure I have developed is appreciated!

It would be easier to help if you could edit your post so it’s formatted correctly, and includes your full configuration, with the variable definitions and locals block - I tried to paste what you have into a config file so I could test it out but that’s not doable as written.

Been working on variations. I’m still having issues addressing the elements of the list properly.


variable "policyset_definition_category" {
  type        = string
  description = "The category to use for all PolicySet defintions"
  default     = "Custom"
}


variable "policyset_definitions" {
  type = list(object({
    set          = string
    name         = string
    policy_type  = string
    display_name = string
    description  = string
    policies = list(object({
      policy_description = string
      effect             = string
    }))
  }))

  description = "List of policy set definitions with a list of policies to be included in the set"

  default = [
    {
      set          = "security"
      name         = "security_governance"
      policy_type  = "Custom"
      display_name = "Security Governance"
      description  = "Contains common Security Governance policies"
      policies = [
        {
          policy_description = "Internet-facing virtual machines should be protected with network security groups"
          effect             = "AuditIfNotExists"
        },
        {
          policy_description = "Subnets should be associated with a Network Security Group"
          effect             = "AuditIfNotExists"
        }
      ]
    },
    {
      set          = "data_protection"
      name         = "data_governance"
      policy_type  = "Custom"
      display_name = "Data Governance"
      description  = "Contains common Data Governance policies"
      policies = [
        {
          policy_description = "Azure Backup should be enabled for Virtual Machines"
          effect             = "AuditIfNotExists"
        },
        {
          policy_description = "Long-term geo-redundant backup should be enabled for Azure SQL Databases"
          effect             = "AuditIfNotExists"
        }
      ]
    },
    {
      set          = "iam"
      name         = "iam_governance"
      policy_type  = "Custom"
      display_name = "IAM Governance"
      description  = "Contains common IAM Governance policies"
      policies = [
        {
          policy_description = "Audit usage of custom RBAC rules"
          effect             = "AuditIfNotExists"
        },
        {
          policy_description = "Custom subscription owner roles should not exist"
          effect             = "AuditIfNotExists"
        }
      ]
    }
  ]
}

resource "azurerm_policy_set_definition" "prod_policy_set" {

  for_each = toset(local.policyset_definitions)

  name         = policyset_definitions.value["name"]
  policy_type  = policyset_definitions.value["policy_type"]
  display_name = policyset_definitions.value["display_name"]
  description  = policyset_definitions.value["description"]

  metadata = <<METADATA
    {
    "category": "${var.policyset_definition_category}"
    }
METADATA

  dynamic policy_definition_reference {
    for_each = local.policyset_definitions.value["policies"]
    content {
      policy_definition_id = policy_definition_reference.value["policy_description"]
      parameters = {
        Effect = policy_definition_reference.value["effect"]
      }
    }
  }
}

Here is the output of the “terraform plan”:


$ terraform plan

Error: Invalid for_each set argument

  on main.tf line 68, in resource "azurerm_policy_set_definition" "prod_policy_set":
  68:   for_each = toset(local.policyset_definitions)

The given "for_each" argument value is unsuitable: "for_each" supports maps
and sets of strings, but you have provided a set containing type object.


Error: Reference to undeclared resource

  on main.tf line 70, in resource "azurerm_policy_set_definition" "prod_policy_set":
  70:   name         = policyset_definitions.value["name"]

A managed resource "policyset_definitions" "value" has not been declared in
the root module.


Error: Reference to undeclared resource

  on main.tf line 71, in resource "azurerm_policy_set_definition" "prod_policy_set":
  71:   policy_type  = policyset_definitions.value["policy_type"]

A managed resource "policyset_definitions" "value" has not been declared in
the root module.


Error: Reference to undeclared resource

  on main.tf line 72, in resource "azurerm_policy_set_definition" "prod_policy_set":
  72:   display_name = policyset_definitions.value["display_name"]

A managed resource "policyset_definitions" "value" has not been declared in
the root module.


Error: Reference to undeclared resource

  on main.tf line 73, in resource "azurerm_policy_set_definition" "prod_policy_set":
  73:   description  = policyset_definitions.value["description"]

A managed resource "policyset_definitions" "value" has not been declared in
the root module.