Terraform output issue from a module that use for_each

Hello!
I’m trying to output the password and policie from my data_json as below:
The idea is use the value from the output of module vault_test in docker module.

  resource "vault_generic_endpoint" "vault_endpoint_test" {
  for_each             = try(var.vault_endpoint_test, {})
  provider             = vault.vault_test
  depends_on           = [vault_auth_backend.userpass_auth]
  path                 = each.value.generic_endpoint_path
  ignore_absent_fields = true

  data_json = <<EOT
{
  "policies": ["${each.value.generic_endpoint_policie}"],
  "password": "${each.value.generic_endpoint_password}"
}
EOT
}

module vault_test output:

output "vault_test_pass" {
  value     = jsondecode(vault_generic_endpoint.vault_endpoint_test[*].data_json)["password"]
  sensitive = true
}

I’m getting the error below

Error: Invalid function argument
  
   on modules/vault_test/output.tf line 12, in output "vault_env_vault_pass":
   12:   value     = jsondecode(vault_generic_endpoint.vault_endpoint_test[*].data_json)["password"]
     ├────────────────
     │ vault_generic_endpoint.vault_endpoint_test is object with 3 attributes
  
 Invalid value for "str" parameter: string required.
  
  
 Error: Unsupported attribute
  
   on modules/vault_test/output.tf line 12, in output "vault_env_vault_pass":
   12:   value     = jsondecode(vault_generic_endpoint.vault_endpoint_test[*].data_json)["password"]
  
 This object does not have an attribute named "data_json".

I tried this as well.

│   on modules/vault_test/output.tf line 12, in output "vault_env_vault_pass":
│   12:   value     = jsondecode(vault_generic_endpoint.vault_endpoint_test.data_json)["password"]
│
│ Because vault_generic_endpoint.vault_endpoint_test has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│     vault_generic_endpoint.vault_endpoint_test[each.key]

map(object) used by for_each

    vault_endpoint_test = {

  "test1" = {
    endpoint_path     = "auth/userpass/users/acctest1"
    endpoint_policie  = "acc-test"
    endpoint_password = "passtest1"
  }

  "test2" = {
    endpoint_path     = "auth/userpass/users/acctest2"
    endpoint_policie  = "acc-test2"
    endpoint_password = "passtest2"
  }

  "test3" = {
    endpoint_path     = "auth/userpass/users/acctest3"
    endpoint_policie  = "acc-test3"
    endpoint_password = "passtes3"
  }
}

The output will be used as input in a different module (docker) to fill:

resource "docker_container" "my_app" { #create 3 containers (test1, test2 and test 3
  for_each = try(var.image_map, {})
  name  = each.key
  image = each.value.image

  env = [
    "VAULT_USERNAME=${value_from_vault_module}", #same as police 
    "VAULT_PASSWORD=${value_from_vault_module}"
  ]

....
}

I’ve tried a lot of different ways without success.
What would be the best approach for this scenario ?
Thanks

I don’t think this will do what you want … quoting from https://developer.hashicorp.com/terraform/language/functions/try:

The try function can only catch and handle dynamic errors resulting from access to data that isn’t known until runtime. It will not catch errors relating to expressions that can be proven to be invalid for any input, such as a malformed resource reference.

Whether a variable named vault_endpoint_test exists is something that can be proven for any input, so the try is not useful here.


There is a more elegant way to write this:

  data_json = jsonencode({
    policies = [each.value.generic_endpoint_policie]
    password = each.value.generic_endpoint_password
  })

It is not valid to use [*] on something that is of map type … quoting from Splat Expressions - Configuration Language | Terraform | HashiCorp Developer

The splat expression patterns shown above apply only to lists, sets, and tuples. To get a similar result with a map or object value you must use for expressions.

Resources that use the for_each argument will appear in expressions as a map of objects, so you can’t use splat expressions with those resources. For more information, see Referring to Resource Instances.

Furthermore, even if [*] did work the way you wanted, you’d then be passing a list to the jsondecode function, which needs a string.

Considering all you are doing here is outputting a value that came from an input variable in the first place, I don’t understand why you are trying to output from the vault_generic_endpoint resource at all… just read the value directly from your input variable vault_endpoint_test instead.

Hi Max,
Thanks for your input! Was very helpful.

If I call the variables from the endpoint in my docker container resource I’ll need to use something to simulate 2 for_each, since I’m using a for_each in my docker resource already, right ?

Sorry, I do not understand what you are asking.

Sorry, it was confusing.

my docker is:

resource "docker_container" "myapp" {
  for_each = var.image_map
  name  = each.key
  image = each.value.image

  env = [
    "VAULT_USERNAME=${value_from_vault}",
    "VAULT_PASSWORD=${value_from_vault}",
  ]
....
}

the values I want for my VAULT_USERNAME and VAULT_PASSWORD are in another list (var.vault_endpoint_test) so, I have 2 different map(objects) that I’d like to use in my docker resource, that’s what I meant.
var.image_map # docker image values for each app
var.vault_endpoint_test # values for each endpoint.

I suppose you can just write

var.vault_endpoint_test[each.key].endpoint_password

then?

Thanks Max, I was over complicating things!
This works fine. :slight_smile: