For Each boolean conditions

Hello, I’m looking for possible solution for my issue, can someone assits on this?
So I would like to use, condition if for_each which is refers to another for_each
Here what I’m trying to do:

resource "aws_lambda_code_signing_config" "lambda" {
   for_each = { for api, name in local.restapi.options.restapi : api => name if lookup(each.value.lambda, "signing", null)}

   ... Rest of the code ... 
}

So as you can see my question is how can I use lookup(each.value.lambda, "signing", null) this boolean

Hi @unity-unity,

If each.value.lambda.signing is itself boolean then what you tried here would be fine except that you’d need to make the lookup fallback value be either true or false rather than null, because null is not a valid boolean value.

Did you try this and encounter a problem? If so, it would help if you could say more about that problem, including the full text of any error messages Terraform returned.

1 Like

Hello, @apparentlymart thank you for helping me. I’ve tried but I got an error:

Error: each.value cannot be used in this context
--
A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.

Ahh, sorry… with the help of the error message it’s easier to see what’s going wrong here.

You can’t use each.value inside the for_each expression because the for_each expression is what decides what each.value will be.

I think it would help to see what you are intending to achieve if you could show an example of what this configuration would look like if you wrote it without using for_each (just write multiple resource blocks, one instance each), and then hopefully I can suggest a way to write a dynamic version of that using for_each.

1 Like

Hello, @apparentlymart

So basically what I want to achieve is:

   restapi = [
      {

        ... Other Restapi Code ... 
        lambdas  = [
            {
               function = "auth"
               signing  = true
               code = {
                  policies    = []
                  publishers  = []
                  description = "some description"
               }
            },
            {
               function = "get"
               signing  = false
               code = {}
        ]
      }

Here is the actual code

locals {
  restapi = {
    init    = {
       self = var.restapi
    }

    options = {
      restapi = lookup(var.parameters, "restapi", [])
    }
  }
}


resource "aws_lambda_function" "lambdas" {
  for_each   = { for api, name in local.restapi.options.restapi : api => name if local.restapi.init.self }
  depends_on = [var.dependency, aws_iam_role.lambdas]

  role             = aws_iam_role.lambdas[0].arn
  filename         = data.archive_file.auth[each.key].output_path
  kms_key_arn      = lookup(each.value.lambda[each.key], "keys", null)
  function_name    = lookup(each.value.lambda[each.key], "function", null)
  handler          = lookup(each.value.lambda[each.key], "handler", null)
  runtime          = lookup(each.value.lambda[each.key], "runtime", null)
  timeout          = lookup(each.value.lambda[each.key], "timeout", null)
  description      = lookup(each.value.lambda[each.key], "description", null)
  source_code_hash = data.archive_file.auth[each.key].output_base64sha256

  # * Environment Variables
  environment {
    variables = lookup(each.value.lambda, "environment", null)
  }

  lifecycle {
    create_before_destroy = true
    ignore_changes        = []
  }


  timeouts {
    create = lookup(var.lifecycles, "create", "20m")
  }
}



resource "aws_lambda_code_signing_config" "lambdas" {
  for_each   = { for api, name in local.restapi.options.restapi : api => name if local.restapi.init.self && if lookup(each.value.lambda, "signing", null) }
  depends_on = [var.dependency, aws_lambda_function.lambdas]

  description = lookup(each.value.lambda.code, "description", null)

  allowed_publishers {
    signing_profile_version_arns = lookup(each.value.lambda.code, "publishers", null)
  }

  policies {
    untrusted_artifact_on_deployment = lookup(each.value.lambda.code, "policies", null)
  }
}



### In Module ###
restapi = true

parameters = {
   restapi = [
      {

        ... Other Restapi Code ... 
        lambdas  = [
            {
               function = "auth"
               signing  = true
               code = {
                  policies    = []
                  publishers  = []
                  description = "some description"
               }
            },
            {
               function = "get"
               signing  = false
               code = {}
        ]
      }
   ]
}

Please note that, all other codes are not pasted here as they will be too much, but I’ve pasted my issue.
Lambda functions is list of objects

Hi @unity-unity,

I’m afraid I’m still not sure what your intended result is. There are too many different parts to this to understand which portion is related to the specific task you want to solve. If you can’t share an unrolled version of the configuration that isn’t using for_each to illustrate, then could you perhaps instead describe your goal in natural language?

For example, I mean something like “declare one lambda function for each something, but only if condition that must hold”. It would be ideal if you can do that by referring to real concepts rather than just to other parts of your configuration, because as I say I’m having trouble understanding what each of the components of your configuration actually represents when thinking from a pure requirements perspective, rather than an implementation perspective.

1 Like

Hello, @apparentlymart
So, what I want is, very clear to me, but I can not reproduce in terraform


I want something like this: This is the pseudo code, so it will not work

resource "aws_lambda_function" "lambdas" {
  for_each   = { for api, name in lookup(var.some.stuff[each.key], "lambda") : api => name }
}

resource "aws_lambda_code_signing_config" "lambdas" {
  for_each   = { for api, name in lookup(var.some.stuff.lambda[each.key], "signing") : api => name if lookup(var.some.stuff.lambda.options[each.key], "signing") == true
}

Issues:
How can I use [each.key]/[count.index], basically I need to get index, so for now
lookup(var.some.stuff.lambda[0], "signing")
This works, but it refers only to first object, I need to use count.index/each.key instead of [0]

Hi @unity-unity,

I think what I’m not understanding is what you are intending each.key to represent in the context of { for api, name in lookup(var.some.stuff[each.key], "lambda") :. What concrete value would var.some.stuff have in this case, and what concrete value would you expect each.key to have? You mentioned 0 in your follow-up text but I don’t really understand where the value zero would come from in this context, because we seem to be iterating over a map.

Again, it would be easier if you could describe this in terms of the underlying problem you want to solve, without using any Terraform syntax at all. If you can describe your goal then I can hopefully suggest Terraform syntax to solve it, but just seeing invalid Terraform syntax without any idea about what you would like it to do leaves me unsure what to suggest.

1 Like

@apparentlymart Let me try to rephrase.

I want dynamic lamda, and each dynamic lambda should contain, dynamic code signing block.

Lambda objects should have condition to create code signing block. For example if I have 2 objects, first object has code signing but second object does not.

I really hope, that now I was clear, and I hope for your help

Okay! I think I understand this now. Thanks for working through this with me.

From your earlier examples it seems like your lambda functions are defined by local.restapi.options.restapi.lambdas. For the sake of focusing only on the task at hand here I’m going to shorten that just to local.lambdas here but still keep your shown structure where each element of lambdas has an attribute function giving the function name and an attribute signing indicating whether it should have a signing object.

So for my simplified example, we’ll say there’s a data structure like this:

locals {
  lambdas = [
    {
      function = "auth"
      signing  = true
    },
    {
      function = "get"
      signing  = false
    },
  ]
}

This data structure seems to meet the first requirement for a for_each expression: it has one element per lambda function instance we want to declare, and each one has a unique key in the form of the function attribute value. Threfore we should be able to use this in the for_each of the lambda function with just one more projection to make it be a map instead of a list, as for_each requires:

resource "aws_lambda_function" "example" {
  for_each = { for l in local.lambdas : l.function => l }

  name = each.value.function
  # ...
}

Your other requirement was to declare one aws_lambda_code_signing_config per function, but only if its corresponding definition has signing set. You can achieve that by copying the for expression from the previous resource and adding an if clause to it:

resource "aws_lambda_code_signing_config" "example" {
  for_each = {
    for l in local.lambdas : l.function => l
    if l.signing
  }

  # ...
}

Writing for l in local.lambdas means that inside the body of the for expression there is a temporary symbol l that refers to each of the element values of local.lambdas in turn. In the original example we used that to access l.function to get the function name, and in this second resource I’ve also added if l.signing, which accesses the signing attribute of the current object. The result then will be a filtered version of the collection which keeps only the elements where l.signing is true.

In a sense we could say that l inside this expression serves a similar function as each.value in the rest of the resource configuration. That follows here because the for expression projects to l.function => l and so the values of the resulting map exactly match the values of the input list. But we can’t use each.value directly inside the for expression, because as I was noting previously it’s this expression that decides what values each.value would take, and so we don’t yet know what value that should return.

Likewise it wouldn’t be valid to use l in the expression that comes directly after in, because that expression is deciding which values l will take. We have to identify a specific single collection here, which the for expression will then take values from.

I hope that makes sense! I tried to cut down the example here so we could focus only on the for expressions and the for_each arguments and not on all of the other seeminly-unrelated parts generating the big local.restapi data structure, although if you have success with my examples here but aren’t sure how to adapt them to work with your existing configuration then I’d be happy to help with that if you can again explain what the goal of each part is, so that I can then suggest a solution that will meet the goal.

1 Like

Thank you, so so much @apparentlymart that is solution what I was looking for

Hi, I hope this topic is not closed, I’ve got a strange behaviour, whenever after apply has been completed, and when I run plan the config_id of resource "aws_lambda_code_signing_config" is changing