3
votes

assign multiple templates files to user_Data variable. we dont want to merge this template in single file due to some architectural pattern .

I'm new to terraform so struggling on this.

data "template_file" "userdata_lin1" {
  template = <<EOF
#!/bin/bash
crontab cronjobfileremote
EOF
}

data "template_file" "userdata_lin2" {
  template = <<EOF
#!/bin/bash
echo "hello"
EOF
}

 user_data  = "${data.template_file.user_data1.rendered}"
2

2 Answers

5
votes

It is a fundamental constraint of most cloud platforms that "user data" or "custom metadata" etc (terminology varies by vendor) is a single opaque string of bytes. The interpretation of those bytes depends on what software you have installed in your virtual machine image that makes use of it.

A common choice of such software is cloud-init. If you are using cloud-init then the "user data" can be provided in a number of different formats.

The main way to provide multiple distinct sections to cloud-init is via a MIME-Multipart archive, which is a concatenation of several values interspersed with headers to allow cloud-init to identify the boundaries and understand how you intend each part to be interpreted.

Because cloud-init is a very common choice of software for interpreting "user data", Terraform has a cloudinit provider which includes a data source for constructing a MIME-Multipart archive.

data "cloudinit_config" "example" {
  gzip          = false
  base64_encode = false

  part {
    content_type = "text/x-shellscript"
    filename = "userdata_lin1"
    content  = <<-EOF
      #!/bin/bash
      crontab cronjobfileremote
    EOT
  }

  part {
    content_type = "text/x-shellscript"
    filename = "userdata_lin2"
    content  = <<-EOF
      #!/bin/bash
      echo "hello"
    EOT
  }
}

You can then set your user_data argument to the output from this data source:

  user_data = data.cloudinit_config.example.rendered

It's important to note that from the perspective of Terraform and from your cloud compute provider, the content of user_data is just an arbitrary string. Any issues in processing the string must be debugged within the target operating system itself, by reading the cloud-init logs to see how it interpreted the configuration and what happened when it tried to take those actions.

4
votes

What I've done in the past is to combine two template files in one.
Like this:

data "template_file" "userdata" {
  template = "${format("%s%s", file("${path.module}/../common.sh"), file("${path.module}/fo.sh"))}"

  vars {
    efs_url     = "${var.efs_url}"
    hostname    = "${data.template_file.hostname.rendered}"
    api_key     = "${var.api_key}"
  }
}

As you can see I'm combining two template files:

  • ../common.sh
  • fo.sh

In my case, this was part of a project with multiple terraform modules common.sh was in the parent folder and contained everything that was needed for all the child modules, then each module added it's own specific requirements.

You can also see that one of the variables for interpolation is also a template_file:
hostname = "${data.template_file.hostname.rendered}"
That is perfectly fine and helps nesting templates. Just in case if you need it...