Hi,
locals {
org_policy_constraints = {
"compute.restrictSharedVpcSubnetworks" = {
allow = local.xpn_subnets_list == [] ? null : local.xpn_subnets_list
deny = local.xpn_subnets_list == [] ? ["all"] : null
}
}
}
resource "google_folder_organization_policy" "policy" {
for_each = local.org_policy_constraints
constraint = "constraints/${each.key}"
folder = var.folder_id
dynamic "list_policy" {
for_each = {
for verb, list in each.value : verb => list
if verb == "allow" || verb == "deny"
}
content {
dynamic "allow" {
for_each = {
for verb, list in each.value : verb => list
if verb == "allow" && list != null
}
content {
all = contains(allow.value, "all") ? true : null
values = ! contains(allow.value, "all") ? allow.value : null
}
}
dynamic "deny" {
for_each = {
for verb, list in each.value : verb => list
if verb == "deny" && list != null
}
content {
all = contains(deny.value, "all") ? true : null
values = ! contains(deny.value, "all") ? deny.value : null
}
}
}
}
}
The objective here is to either create an allow OR deny block underneath the list_policy block.
I cannot figure out why it systematically generates the following error, when the input map local.org_policy_constraints contains one null element + one non null element:
Error: list_policy: attribute supports 1 item maximum, config has 2 declared
What am I missing here? Any constructive comment would be greatly appreciated.
Thanks in advance.
1 Like
Formalizing it for this post made me realize my mistake, right in front of me the whole time: i was missing the list != null check on the parent nested block…
dynamic "list_policy" {
for_each = {
for verb, list in each.value : verb => list
if (verb == "allow" || verb == "deny") && list != null
}
Thanks for being such a good listener Forum, much appreciated!
1 Like
Thanks for sharing this solution, @sleterrier!
There is another potential cause of this situation which doesn’t seem to apply in your case but I want to share it in case someone else finds this thread in future and can therefore try both of these solutions.
During the plan phase Terraform often needs to work with incomplete information because certain values cannot be known until after the planned changes have been applied. In such cases, Terraform will interpret the partial information as best it can but it must sometimes make conservative assumptions in order to work within the language expectations.
One example of this is when the for_each
expression in a dynamic
block uses an unknown value (that is, a value that won’t be known until apply) as part of its computation. If that were true for your dynamic "allow"
and dynamic "deny"
blocks in the example you shared, Terraform would conservatively assume that both blocks would be present during planning, which might trigger a validation rule that only one of the two can be present. (I don’t know if such a rule exists; just using this as an example.)
For that reason, it’s best to ensure that your for_each
expressions only make use of values decided statically in the configuration. That can include both constant values written directly, like ["foo"]
, but it can also include any expression derived from constant values elsewhere in the configuration, such as a reference to an input variable whose value is set statically in the calling module.
For dynamic
blocks in particular Terraform will try to make the best of a partially-unknown situation, but it will often err on the side of reporting an error if there is the possibility that a problem will occur during plan.
1 Like