Evaluate dynamic statement with for_each

I’m trying to conditionally create a Dynamic statement, by checking if a variable is empty or not, while checking for some Regex patterns.

data "aws_organizations_organization" "this_org" {}

dynamic "statement" {
    for_each = [for y in var.arns_with_access : y if length(regexall("\\*+", y)) == 0]
    content {
      actions = [
        "kms:DescribeKey"
      ]
      effect = "Allow"
      principals {
        type        = "AWS"
        identifiers = [statement.value]
      }
      resources = ["*"]
      condition {
        test     = "StringEquals"
        variable = "aws:PrincipalOrgID"
        values = [
          data.aws_organizations_organization.this_org.id,
        ]
      }
    }
  }

That statement should only evaluate if var.arns_with_access isn’t empty.

for_each = var.arns_with_access != "" { 
    for y in var.arns_with_access : y if length(regexall("\\*+", y)) == 0
} : 0

The above test does not work. Any suggestions, please?

Hi @manu.nz,

There are a few things about the example you shared which seem confusing to me:

  • You test var.arns_with_access != "", implying that var.arns_with_access is a string, but then you use it in for y in var.arns_with_access which implies that the variable has a collection type.
  • If your for_each expression was intended to be a conditional expression, I notice that there is no ? symbol after the first condition expression.
  • Your for expression is delimited by braces { } and so will generate a mapping, but you’ve only provided one element expression y. To generate a mapping you need to specify key => value, where key and value are both expressions.
  • The second result operand of your conditional is the expression 0, which cannot possibly match the type of a for expression with braces, since 0 is a number.

Because of the above I’m not entirely sure what your goal is here, but I can make a few guesses:

  • var.arns_with_access is a list or set, rather than a string.
  • Your content block doesn’t mention statement.key so it seems that you don’t really need to set for_each to a mapping, so we can use a sequence for, with brackets [ ] instead.
  • If var.arns_with_access is an empty list, you’d like to generate zero statement blocks.

With those assumptions in mind, I would suggest the following as a starting point:

  for_each = [
    for y in var.arns_with_access : y
    if length(regexall("\\*+", y)) == 0
  ]

After writing this out I realized I essentially just reproduced your original example, which suggests to me that I guessed incorrectly about what your goal is. If so, can you say some more about why this simpler expression isn’t sufficient for what you need?

Sorry for the misunderstanding and lack of information.

var.arns_with_access is a list(string):

variable "arns_with_access" {
  type        = list(string)
  default     = []
}

The original code - I believe is checking if there is any wildcard (*) in the value and if there is, it won’t evaluate the dynamic statement? - is:

for_each = [for y in var.arns_with_access : y if length(regexall("\\*+", y)) == 0]

My goal is to keep the original condition, but at the same time make data.aws_organizations_organization.this_org conditional so it doesn’t evaluate if var.arns_with_access is empty.

Hi @manu.nz,

A for expression with an if clause will never generate a resulting collection that has more elements than the input collection – var.arns_with_access in your case.

So if we consider the example I included in my previous response:

  for_each = [
    for y in var.arns_with_access : y
    if length(regexall("\\*+", y)) == 0
  ]

If var.arns_with_access has zero elements (i.e. it’s empty) then the result of this for expression must also necessarily be empty, since there are no elements for it to consider with the if condition. So this expression already seems to achieve the goal you stated: if your var.arns_with_access is empty then your for_each argument will also be an empty list, and so the dynamic block will generate zero statement blocks.