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.
Adding new questions to an old topic doesn’t typically get good results, because your new question doesn’t include any actionable details about your situation.
If you have a similar-sounding problem, please start a new topic and describe what exactly you were trying to achieve, what you tried, and what behavior you observed when you tried it, ideally including configuration examples and full error messages when relevant. Feel free to link back to an earlier topic if you think it’s connected, but that’s no substitute for describing what you’re seeing from your own perspective, because what you have encountered may not be as similar to this other topic as you think it is.