How to handle known-after-appy in condition?

Dear all,
I have condition to deal with, which I cannot figure out how.
I have to add NACL rules for AWS gateway VPCEs, based on two variables:

variable "vpc_endpoints_gws" {
  type        = map(any)
  default     = {}
  description = "List of Gateway Endpoints"
}
variable "allow_gw_eps" {
  type        = bool
  description = "Should NACL rules be added for Gateway EPs"
}

if allow_gw_eps is true and the values for vpc_endpoints_gws is not supplied, then it will use the aws_vpc_endpoint data-resource to get the the values, otherwise it will use the values supplied by vpc_endpoints_gws:

data "aws_vpc_endpoint" "gw_eps" {
  for_each = var.allow_gw_eps && var.vpc_endpoints_gws == tomap({}) ? toset([
    "s3", "dynamodb"
  ]) : []
  vpc_id       = local.my_vpc_info.id
  service_name = "com.amazonaws.${var.region}.${each.value}"
}

The NACL rules creation is is part of my subnet sub-module, where I pass the vpc_endpoints_gws to it and my plan to based on if it’s supplying a null/{} value or not, it will do the aws_network_acl_rule resource or skip it.

do that, I have this two local variables:

data_gw_eps = var.allow_gw_eps && var.vpc_endpoints_gws == tomap({}) ? data.aws_vpc_endpoint.gw_eps : {}
vpc_gw_eps  = var.vpc_endpoints_gws != tomap({}) ? var.vpc_endpoints_gws : local.data_gw_eps

and pass that to the subnet module:

module "dbs_subnet" {
  source       = "/home/xxxxxxxxx/tf_modules/include/subnet"
  aws_region   = var.region
  ......
  ......
  vpc_id       = local.my_vpc_info.id
  vpc_gw_eps   = local.vpc_gw_eps
}

Then in the subnet module, I have this two local variables to feed in to aws_network_acl_rule resource:

 gw_ep_cidrs = var.vpc_gw_eps == tomap({}) ? null : flatten([
    for ep in keys(var.vpc_gw_eps[var.vpc_name]) :
    var.vpc_gw_eps[var.vpc_name][ep].cidr_blocks
  ])

  gw_eps_nacl = local.gw_ep_cidrs == null ? null : flatten([
    for type in ["egress", "ingress"] : [
      for idx, cdr in sort(local.gw_ep_cidrs) : {
        "cidr" = cdr,
        "rule" = 200 + (2 * idx),
        "type" = type,
      }
    ]
  ])

When allow_gw_eps = false, things work okay but when it’s true, I get this error:

Error: Invalid index

on .terraform/modules/dbs_subnet/variables.tf line 20, in locals:
20: for ep in keys(var.vpc_gw_eps[var.vpc_name]) :
|----------------
| var.vpc_gw_eps is map of object with 2 elements
| var.vpc_name is “main”

The given key does not identify an element in this collection value.

Does any one know why I’m getting this? The data-source should have return the value by that time, right? What am I doing wrong here?

in console, I see both of the local variables are returning (known after apply):

> var.allow_gw_eps
true
> local.data_gw_eps
(known after apply)
> local.vpc_gw_eps
(known after apply)
>

But that shouldn’t be the issue. Using TF v0.14.10

1 Like

Hi @dsantanu,

I’m not sure I followed through all of this just yet but I did notice something I wanted to point out in case it helps. If not, we can perhaps dig a little deeper.

I see you using expressions like var.vpc_endpoints_gws == tomap({}) in some places. This is a risky way to express “is the map empty?” because the == operator can return true only for two values whose types are identical. Your variable seems to be defined as type = map(any), which means that Terraform will infer the map’s element type dynamically based on what value you assign to that variable. For example, if you were to pass in {"a" = "b"} then Terraform would infer map(string) as the type of that variable, because "b" is a string value.

The type of tomap({}) is uncertain because you didn’t write any elements for it to infer based on. Therefore there’s no guarantee that these two values end up having the same type, and thus the comparison might not be a valid one.

A more robust way to write down “map is empty” is to test the length of the map: length(var.vpc_endpoints_gws) == 0. Terraform knows that length only considers the number of elements and doesn’t care about any of the keys and value types, so a test like this can succeed regardless of which specific type your variable is inferred to have.

Valid point and duly noted. Let me change that, try again and report back. I don’t think my actual issue will be resolved by this but let me get back to you.

-S