Use of the lookup function for an attribute name replacement

Hi folks,

I’m using terraform version v1.1.5 and am getting this error when tring to populate an output variable attribute name using a lookup function. What foolish thing am I doing wrong here please?

Error

 Error: Invalid attribute name
│ 
│   on ../modules/db_option_group/outputs.tf line 9, in output "mysql_db_option_group_arn":
│    9:   value       = element(concat(aws_db_option_group.%{lookup(var.option_group_arn_map,var.engine_name)}.*.arn, [""]), 0)
│ 
│ An attribute name is required after a dot.

code in my outputs.tf

output "mysql_db_option_group_arn" {
  description = "The ARN of the mysql db option group"
  #value       = element(concat(aws_db_option_group.mysql.*.arn, [""]), 0)
  value       = element(concat(aws_db_option_group.%{lookup(var.option_group_arn_map,var.engine_name)}.*.arn, [""]), 0)
}

And this in my variables.tf

variable "option_group_arn_map" {
  type = map
  default = {
    "mysql" = "mysql"
    "mariadb" = "mysql"
    "sqlserver-ee" = "sqlserver"
    "sqlserver-ex" = "sqlserver"
    "sqlserver-se" = "sqlserver"
    "sqlserver-web" = "sqlserver"
    "oracle-ee" = "oracle"
    "oracle-se2" = "oracle"
  }
}

Thanks
Steve

Hi @sjwood ,

What if you use the index syntax instead?

  value = element(concat(aws_db_option_group["%{lookup(var.option_group_arn_map,var.engine_name)}"].*.arn, [""]), 0)

Hi

So if this is what you meant that’s not worked so far, I tried the double %% to escape the character too

│ Error: Invalid template control keyword
│ 
│   on ../modules/db_option_group/outputs.tf line 35, in output "db_option_group_arn":
│   35:   value        = element(concat(aws_db_option_group["%{lookup(var.option_group_arn_map,var.engine_name)}"].*.arn, [""]), 0)
│ 
│ "lookup" is not a valid template control keyword.

 Error: Invalid reference
│ 
│   on ../modules/db_option_group/outputs.tf line 35, in output "db_option_group_arn":
│   35:   value        = element(concat(aws_db_option_group["%%{lookup(var.option_group_arn_map,var.engine_name)}"].*.arn, [""]), 0)
│ 
│ A reference to a resource type must be followed by at least one attribute access, specifying the resource name.

terraform -v  
Terraform v1.1.5
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v3.70.0
+ provider registry.terraform.io/hashicorp/random v3.1.0```

Hi @sjwood,

The main thing I notice about the example you shared is this invalid expression:

aws_db_option_group.%{lookup(var.option_group_arn_map,var.engine_name)}

This is not valid Terraform language grammar, but I assume what you were hoping would happen here is that Terraform would evaluate the expression you gave there and then use it as a resource name.

That isn’t possible in Terraform because the dependency-graph-based evaluation model requires Terraform to be able to see before evaluating any expressions which objects refer to which other objects; to allow something like what you tried here would mean that Terraform would have no way to know which resource output "mysql_db_option_group_arn" should depend on.

However, it looks like your underlying use-case here is to look up one of possibly many resources based on a key. You can achieve that by constructing a lookup table and then referring to that lookup table:

locals {
  option_groups = {
    mysql         = one(aws_db_option_group.mysql)
    mariadb       = one(aws_db_option_group.mysql)
    sqlserver-ee  = one(aws_db_option_group.sqlserver)
    sqlserver-ex  = one(aws_db_option_group.sqlserver)
    sqlserver-se  = one(aws_db_option_group.sqlserver)
    sqlserver-web = one(aws_db_option_group.sqlserver)
    oracle-ee     = one(aws_db_option_group.oracle)
    oracle-se2    = one(aws_db_option_group.oracle)
  }
}

output "mysql_db_option_group_arn" {
  description = "The ARN of the mysql db option group"
  value       = local.option_groups[var.engine_name].arn
}

Here I’ve made some assumptions about what the rest of your configuration looks like in order to simplify the expression you included in your original example. In particular, I’m guessing that these three aws_db_option_group resources each have a conditional count which declares either zero or one instances, and so I used one to concisely convert that to a value that might be null, which therefore makes the final output value argument conderably simpler.

Notice that the output value now refers to local.option_groups rather than to any resource in particular. That local value in turn refers to all three of the aws_db_option_group resources, and so Terraform can see that all three of those must be complete before Terraform will try to evaluate local.option_groups, which creates a correct dependency graph.

2 Likes

Thank you for such a thorough and well explained answer, that works!