Inappropriate value for attribute "vars"

Is there a way to define ‘control_nodes’ in datasources.tf so that it does not hit the following error?

From variables.tf:

variable "control_nodes" {
        description = "A list of IP addresses of control nodes"
}
│ Error: Incorrect attribute value type
│
│   on modules/control/datasources.tf line 12, in data "template_file" "control":
│   12:         vars = {
│   13:                 control_nodes = var.control_nodes
│   14:         }
│
│ Inappropriate value for attribute "vars": element "control_nodes": string required.

You should be able to fix this by adding

type = string

Or setting a default value that is a string. I’m fairly certain this is failing because it does not correctly determine the variables type.

I tried that earlier.

Invalid value for "seqs" parameter: all arguments must be lists or tuples; got string.

I don’t see seqs in the above snippets. Was it truncated?

From the script, which is rendered that was not included.

--nodes=${join(",", formatlist("%s:80", ${control_nodes}))}

I’m still not seeing where seqs is getting assigned a value. That said, here’s a wrapper I use in situations like this.

try(compact(split( ",", replace(var.control_nodes," ",""))),[])

You can probably drop the replace, I use that as a chomp in my use cases.

It does not fail during plan as previously but fails during apply.

 Inappropriate value for attribute "vars": element "worker_nodes": string required.

Hi @mick_a_thompson,

It seems like you’re using the deprecated template_file data source, which is limited only to working with strings because it was designed for an earlier version of Terraform which had that limitation.

To get the result you wanted, I’d suggest using the templatefile function instead. It serves a similar purpose to the template_file data source but it’s built in to the Terraform language rather than being delegated to a provider, and it’s designed to support the full set of types that the Terraform language supports.

Since you didn’t share your full data "template_file" "control" block I can’t offer a drop-in replacement for it, but here’s a starting point of using a local value to capture a template which hopefully you can see how to adapt from your existing configuration:

locals {
  control_script = templatefile("${path.module}/example.tpl", {
    control_nodes = var.control_nodes
  })
}

With the above in place, you can replace existing mentions of data.template_file.control.rendered with local.control_script. If you only use this result in one place then you could also consider not declaring a named local value at all, and instead just calling templatefile directly from whichever resource argument needs the template result.

I came across this function before but couldn’t quite get the syntax to make it work for me; I was placing it in the datasources.tf. Below is a better snippet of the datasource file. Mostly in all my files the variables are strings but I have three variables that exhibit this behaviour (I am refactoring an older, existing configuration). I’d prefer to keep everything in datasource files for consistency, can I do so?

data "template_file" "proxy" {
        template = file("${path.module}/files/proxy.conf")

        vars = {
                proxy = var.proxy
        }
}

data "template_file" "control" {
        template = file("${path.module}/files/control.sh")

        vars = {
                os_version = var.version
                apiserver_ip = var.server_ip
                environment_name = var.environment_name
                control_nodes = var.control_nodes
         }
}

Terraform v1.0.0
The control_nodes values are unknon until deploy.

The following should be equivalent to those two data blocks, aside from templatefile's ability to work with non-string values:

locals {
  proxy_conf = templatefile("${path.module}/files/proxy.conf", {
    proxy = var.proxy
  })
  control_sh = templatefile("${path.module}/files/control.sh", {
    os_version       = var.version
    apiserver_ip     = var.server_ip
    environment_name = var.environment_name
    control_nodes    = var.control_nodes
  })
}

You can place this locals block in any file you wish, including datasources.tf if you like although templatefile is not actually a data source, so that might be confusing to future maintainers depending on what your typical conventions are.

Since a local value uses different reference syntax than a data resource you’ll also need to update references elsewhere in the module, like this:

  • data.template_file.proxy.rendered becomes local.proxy_conf
  • data.template_file.control.rendered becomes local.control_sh

The var.control_nodes value not being known until apply is not a problem for Terraform, but it does mean that the value of local.control_sh also won’t be known until apply, and so if you use that value as part of a resource configuration you won’t be able to see the rendered result as part of the plan. That’s a typical situation for template rendering with unknown values and would’ve been true for the template_file data source too.

1 Like

Thanks for the assistance. It looks like what I tried but I’ll try this on Monday. I’ll let you know how it goes.

I took on board your comment “might be confusing to future maintainers” and placed it in locals.tf.

Something still not right though.

[vagrant@host7 terraform-control]$ terraform validate

╷
│ Error: Error in function call
│
│   on modules/control/locals.tf line 6, in locals:
│    6:      control_sh = templatefile("${path.module}/files/control.sh", {
│    7:                 os_version = var.os_version
│    8:                 apiserver_ip = var.apiserver_ip
      9:                 environment_name = var.environment_name
│   10:                 control_nodes = var.control_nodes
│   11:                 }
│   12:         )
│     ├────────────────
│     │ path.module is "modules/control"
│     │ var.apiserver_ip is a string, known only after apply
│     │ var.controle_nodes will be known only after apply
│     │ var.environment_name is a string, known only after apply
│     │ var.os_version is a string, known only after apply

│ Call to function "templatefile" failed: modules/control/files/control.sh:18,168-169: Invalid character; This character is not used within the language., and 3 other diagnostic(s).

It does not like the following in the control.sh script

--nodes=${join(",", formatlist("%s:80", ${control_nodes}))}

I removed the braces around control_nodes and all good.

Thanks for your assistance!!

I have the same issue, like:
Error: Incorrect attribute value type

│ on 7-kubeadm.tf line 12, in data “template_file” “worker-userdata”:
│ 12: vars = {
│ 13: k8stoken = “{var.k8stoken}" │ 14: masterIP = "{aws_instance.controller_etcd..private_ip}"
│ 15: }
│ ├────────────────
│ │ aws_instance.controller_etcd is tuple with 1 element
│ │ var.k8stoken is ******************************

│ Inappropriate value for attribute “vars”: element “masterIP”: string required.


│ Error: Incorrect attribute value type

│ on 7-kubeadm.tf line 21, in data “template_file” “etcd-userdata”:
│ 21: vars = {
│ 22: k8stoken = "{var.k8stoken}" │ 23: masterIP = "{aws_instance.controller_etcd.
.private_ip}”
│ 24: }
│ ├────────────────
│ │ aws_instance.controller_etcd is tuple with 1 element
│ │ var.k8stoken is ***************************

│ Inappropriate value for attribute “vars”: element “masterIP”: string required.

terraform plan

var.k8stoken
Enter a value: ******************************


│ Error: Invalid reference

│ on 7-kubeadm.tf line 13, in data “template_file” “worker-userdata”:
│ 13: type = string

│ A reference to a resource type must be followed by at least one attribute access, specifying the resource name.