I've searched quite a bit and don't think I've found the answer I really need. I'm trying to loop through a nested block and am successful in doing this if all of the attributes are on the same root object. This is great if I want to loop over the entire set of attributes. However this situation is a bit different. I need to loop over an entire set of attributes and also a sub-set.
In this Terragrunt example, you can see the desired inputs since we want to loop over the escalation policy entirely as well as loop the rule and its targets so that we can create many escalation policies with many rules/targets in them.
/// PagerDuty Escalation Policies
create_escalation_policy = true
escalation_policies = [
{
name = "TEST Engineering Escalation 1"
description = "My TEST engineering escalation policy 1"
teams = ["111N1CV"]
num_loops = 2
rule = [
{
escalation_delay_in_minutes = 15
target = {
type = "user_reference"
id = "ABCB8F3"
}
},
{
escalation_delay_in_minutes = 15
target = {
type = "user_reference"
id = "NBCB1A1"
}
}
}
]
However, after quite a bit of trial and error, I'm able to loop over the entire escalation policy but not if we have values inside of rule = {
which returns a generic error that Terraform can't find those attributes in the object which I have confirmed is the root object instead of the nested one. This was validated by simply moving those attributes out to the root of the object input block.
│ Error: Unsupported attribute
│
│ on main.tf line 121, in resource "pagerduty_escalation_policy" "this":
│ 121: id = rule.value.id
│ ├────────────────
│ │ rule.value is object with 5 attributes
│
│ This object does not have an attribute named "id".
For reference, here is the variable for var.escalation_policies
variable "escalation_policies" {
description = "A list of escalation policies and rules for a given PagerDuty service."
type = any
}
and the resource
resource "pagerduty_escalation_policy" "this" {
for_each = {
for key in var.escalation_policies : key.name => {
name = key.name
description = key.description
num_loops = key.num_loops
teams = key.teams
}
if var.create_escalation_policy == true
}
name = each.value.name
description = each.value.description
num_loops = each.value.num_loops
teams = each.value.teams
dynamic "rule" {
for_each = {
for k, v in var.escalation_policies : k => v }
content {
escalation_delay_in_minutes = rule.value.escalation_delay_in_minutes
target {
type = rule.value.type
id = rule.value.id
}
}
}
}
With your current example the dynamic "rule"
block has a for
expression that isn't really doing anything useful:
{ for k, v in var.escalation_policies : k => v }
This expression is strange in two ways:
k, v
directly to k => v
because that doesn't really change anything about the key or the value. Since your source var.escalation_policies
is a list rather than a map this is changing the data type of the result and making Terraform convert the integer indices to strings instead, but otherwise the elements are the same as var.escalation_policies
.pagerduty_escalation_policy.this
instance per var.escalation_policy
element and then each one will have one nested rule
block for each of your escalation policies.To get a useful result the for_each
in your dynamic
block should use a different collection as the basis for its repetition. I think in your case you're intending to use the nested lists inside the rule
attributes of each of your policies, but your outermost for_each
expression doesn't include the rules so you'll first need to update that:
resource "pagerduty_escalation_policy" "this" {
for_each = {
for policy in var.escalation_policies : policy.name => {
name = policy.name
description = policy.description
num_loops = policy.num_loops
teams = policy.teams
rules = policy.rule
}
if var.create_escalation_policy == true
}
# ...
}
This means that each.value
will now include an additional attribute rules
which has the same value as the corresponding attribute in each element of var.escalation_policies
.
You can then refer to that rules
attribute in your dynamic
block:
dynamic "rule" {
for_each = each.value.rules
content {
escalation_delay_in_minutes = rule.value.escalation_delay_in_minutes
target {
type = rule.value.target.type
id = rule.value.target.id
}
}
}
This tells Terraform to generate a dynamic rule
block for each element of each.value.rules
, which is the rules
attribute for the current policy.
Inside the content
block rule.value
is the current rule
object, so you can refer to attributes like escalation_delay_in_minutes
and target
from that object.