Search code examples
terraformaws-lambda-layers

How to select specific instance of an for_each object in terraform


In module us-region-1, I define a aws_lambda_layer_version

Layers in module resource:

resource aws_lambda_layer_version "abc"{
  for_each         = ".xxxx."
  layer_name = each.key
}

And in global main terraform, I define an lamba layers to reference to that aws_lambda_layer_version, so I have to define an data resource to get reference and depend on module resource

Data layers

data aws_lambda_layer_version "abc"{
  for_each         = "..."
  layer_name = each.key
  depends_on = [??????]
  # `aws_lambda_layer_version.abc` not work because it's not choose specific instance of aws_lambda_layer_version.abc.
}

Lambda function

resource "aws_lambda_function" "test-lambda-layers" {
  for_each         = ".xxxx."
  layers           = [data.aws_lambda_layer_version.abc[each.key].arn]
}


Solution

  • Dependencies in Terraform are between resources as a whole, and not between individual instances of resources. This is because the count and for_each arguments can themselves create dependencies, and so Terraform can decide the instances for a resource only after it's begun walking the dependency graph.

    With that said, it's not clear to me from your question why it's important to depend on specific instances in your case. Dependencies only affect the order of operations and not which operations Terraform will perform, and so a dependency on all of the instances of aws_lambda_layer_version.abc should be an acceptable substitute for a dependency on an individual instance: it'll still provide the same guarantee that the dependent will be processed only after the thing it depends on, it'll just also wait until some other unrelated objects were complete first.

    resource "aws_lambda_layer_version" "abc" {
      for_each = ...
    
      layer_name = each.key
    }
    
    data "aws_lambda_layer_version" "abc" {
      for_each = aws_lambda_layer_version.abc
    
      layer_name = each.value.layer_name
    }
    

    With that said, this updated configuration still exhibits a Terraform anti-pattern: a particular Terraform configuration should typically either manage a particular object or read that object from the remote API, never both at the same time. From the rest of your question it seems like you need to use the ARN of the AWS Lambda Layer Version object, in which case you can access the equivalent attribute from the managed resource type directly, rather than asking Terraform to read back the same object it's already managing:

    resource "aws_lambda_function" "example" {
      for_each = aws_lambda_layer_version.abc
    
      layers = [each.value.arn]
    }
    

    If your motivation for this was to spread these declarations between modules, the better way to do that would be to pass the data directly between the modules, rather than using a data resource as an indirection. In your module that declared aws_lambda_layer_version.abc you can write an output value like this:

    output "lambda_layer_versions" {
      value = tomap({
        for k, lv in aws_lambda_layer_version.abc : k => {
          id  = lv.id
          arn = lv.arn
        }
      })
    }
    

    This will construct a map of objects which has the same keys as your for_each instance keys, where each object has id and arn attributes.

    Your root module can then access the ARNs from this object:

    module "layers" {
      source = "./module/lambda-layers"
    
      # ...
    }
    
    resource "aws_lambda_function" "test-lambda-layers" {
      for_each = module.layers.lambda_layer_versions
    
      layers = [each.value.arn]
    }