<p>Here is how I got around it, I declared these as HDL object,</p>
<pre><code>data &quot;template_file&quot; &quot;user_data&quot; {
  template = templatefile(
    &quot;cloud/cloud.cfg&quot;,
    {
      ssh_keys = {
        ssh_ecdsa_key_private  = file(&quot;./ssh/acme-bitbucket-id_ecdsa&quot;)
        ssh_ecdsa_key_public   = file(&quot;./ssh/acme-bitbucket-id_ecdsa.pub&quot;)
      }
    }
  )
}
</code></pre>
<p>And then I pulled it into the yaml file using <a href="https://www.terraform.io/language/functions/yamlencode" rel="nofollow noreferrer"><code>yamlencode</code></a>.</p>
<p>There is a bit of documentation that covers this, over at <a href="https://www.terraform.io/language/functions/templatefile#generating-json-or-yaml-from-a-template" rel="nofollow noreferrer"><em>&quot;Generating JSON or YAML from a template&quot;</em></a></p>

CC BY-SA 4.0
2023-06-05T15:42:52.433Z
Ok, I got this far in the docs as well -- but where is the `yamlencode` called from? Within the template?
ms-ati
CC BY-SA 4.0
2022-03-18T06:47:55.990Z
2022-03-18T06:47:55.990Z
Evan Carroll
<p>By the way if you want to use yaml for your cloud-init template you can achieve what you want using <a href="https://developer.hashicorp.com/terraform/language/functions/indent" rel="nofollow noreferrer"><code>indent</code> function</a></p>
<pre class="lang-yaml prettyprint-override"><code>ssh_keys:
  rsa_private: |
    ${indent(4, &quot;${private_key}&quot;)}
</code></pre>
<p>Make sure to adjust the indented spaces as per your template.</p>

CC BY-SA 4.0
2023-06-22T11:58:20.887Z
2023-06-22T11:58:20.887Z
Trayan Azarov
<p>I am using Terraform. Terraform has a notion <a href="https://www.terraform.io/language/functions/templatefile" rel="nofollow noreferrer">templatefile</a> which can be used to take variables that Terraform has access to (locally supplied or retrieved externally) and to apply them to a Jinja2 template. <a href="https://learn.hashicorp.com/tutorials/terraform/cloud-init" rel="nofollow noreferrer">This specific example is very close to the one in the documentation</a>.</p>
<p>People use these templates to generate cloud-init files with Jinja2 templates. Here is a snipped of the template,</p>
<pre class="lang-yaml prettyprint-override"><code>ssh_keys:
    ecdsa_private: &quot;${ssh_ecdsa_key_private}&quot;
</code></pre>
<p>Then these variables get set in Terraform, like this,</p>
<pre><code>data &quot;template_file&quot; &quot;user_data&quot; {
  template = templatefile(
    &quot;cloud/cloud.cfg&quot;,
    {
      ssh_ecdsa_key_private  = file(&quot;./ssh/acme-bitbucket-id_ecdsa&quot;)
    }
  )
</code></pre>
<p>This is however problematic for me presumably because cloud-init is yaml.</p>
<pre><code>  in &quot;&lt;unicode string&gt;&quot;, line 22, column 1:
    &quot;
    ^
could not find expected ':'
  in &quot;&lt;unicode string&gt;&quot;, line 24, column 8:
          &quot;-----BEGIN OPENSSH PRIVATE KEY-----
           ^&quot;
2022-03-18 00:20:32,611 - util.py[WARNING]: Failed at merging in cloud config part from part-001
2022-03-18 00:20:32,611 - util.py[DEBUG]: Failed at merging in cloud config part from part-001
Traceback (most recent call last):
  File &quot;/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py&quot;, line 140, in handle_part
    self._merge_part(payload, headers)
  File &quot;/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py&quot;, line 116, in _merge_part
    (payload_yaml, my_mergers) = self._extract_mergers(payload, headers)
  File &quot;/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py&quot;, line 95, in _extract_mergers
    mergers_yaml = mergers.dict_extract_mergers(payload_yaml)
  File &quot;/usr/lib/python3/dist-packages/cloudinit/mergers/__init__.py&quot;, line 79, in dict_extract_mergers
    raw_mergers = config.pop('merge_how', None)
AttributeError: 'NoneType' object has no attribute 'pop'
2022-03-18 00:20:32,616 - __init__.py[DEBUG]: Calling handler CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']] (__end__, None, 3) with frequency once-per-instance
</code></pre>
<p>I believe what's happening here is the file isn't being indented properly. Let's say <code>./ssh/acme-bitbucket-id_ecdsa</code> looked like this,</p>
<pre><code>-----BEGIN OPENSSH PRIVATE KEY-----
FOO
BAR
BAZ
-----END OPENSSH PRIVATE KEY-----
</code></pre>
<p>I think that's getting processed into the template like this,</p>
<pre class="lang-yaml prettyprint-override"><code>ssh_keys:
    ecdsa_private: -----BEGIN OPENSSH PRIVATE KEY-----
FOO
BAR
BAZ
-----END OPENSSH PRIVATE KEY-----
</code></pre>
<p>What is the way to stop properly produce a <code>cloud-init</code> file with Terraform?</p>

<terraform><cloud-init>
Using a terraform template to produce a cloud-init yaml: template variables do not respect yaml context/whitespace
Evan Carroll
# Using a terraform template to produce a cloud-init yaml: template variables do not respect yaml context/whitespace

(Asked by: username_0 on 2022-03-18)

I am using Terraform. Terraform has a notion templatefile which can be used to take variables that Terraform has access to (locally supplied or retrieved externally) and to apply them to a Jinja2 template. This specific example is very close to the one in the documentation.
People use these templates to generate cloud-init files with Jinja2 templates. Here is a snipped of the template,
ssh_keys:
    ecdsa_private: "${ssh_ecdsa_key_private}"

Then these variables get set in Terraform, like this,
data "template_file" "user_data" {
  template = templatefile(
    "cloud/cloud.cfg",
    {
      ssh_ecdsa_key_private  = file("./ssh/acme-bitbucket-id_ecdsa")
    }
  )

This is however problematic for me presumably because cloud-init is yaml.
  in "", line 22, column 1:
    "
    ^
could not find expected ':'
  in "", line 24, column 8:
          "-----BEGIN OPENSSH PRIVATE KEY-----
           ^"
2022-03-18 00:20:32,611 - util.py[WARNING]: Failed at merging in cloud config part from part-001
2022-03-18 00:20:32,611 - util.py[DEBUG]: Failed at merging in cloud config part from part-001
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py", line 140, in handle_part
    self._merge_part(payload, headers)
  File "/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py", line 116, in _merge_part
    (payload_yaml, my_mergers) = self._extract_mergers(payload, headers)
  File "/usr/lib/python3/dist-packages/cloudinit/handlers/cloud_config.py", line 95, in _extract_mergers
    mergers_yaml = mergers.dict_extract_mergers(payload_yaml)
  File "/usr/lib/python3/dist-packages/cloudinit/mergers/__init__.py", line 79, in dict_extract_mergers
    raw_mergers = config.pop('merge_how', None)
AttributeError: 'NoneType' object has no attribute 'pop'
2022-03-18 00:20:32,616 - __init__.py[DEBUG]: Calling handler CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']] (__end__, None, 3) with frequency once-per-instance

I believe what's happening here is the file isn't being indented properly. Let's say ./ssh/acme-bitbucket-id_ecdsa looked like this,
-----BEGIN OPENSSH PRIVATE KEY-----
FOO
BAR
BAZ
-----END OPENSSH PRIVATE KEY-----

I think that's getting processed into the template like this,
ssh_keys:
    ecdsa_private: -----BEGIN OPENSSH PRIVATE KEY-----
FOO
BAR
BAZ
-----END OPENSSH PRIVATE KEY-----

What is the way to stop properly produce a cloud-init file with Terraform?

**Tags:** terraform, cloud-init


---

## 2 answers

### Answer by username_0 on 2022-03-18. Score: 0 | Accepted answer

Here is how I got around it, I declared these as HDL object,
data "template_file" "user_data" {
  template = templatefile(
    "cloud/cloud.cfg",
    {
      ssh_keys = {
        ssh_ecdsa_key_private  = file("./ssh/acme-bitbucket-id_ecdsa")
        ssh_ecdsa_key_public   = file("./ssh/acme-bitbucket-id_ecdsa.pub")
      }
    }
  )
}

And then I pulled it into the yaml file using yamlencode.
There is a bit of documentation that covers this, over at "Generating JSON or YAML from a template"

> **Comments:**
> **username_1**: Ok, I got this far in the docs as well -- but where is the `yamlencode` called from? Within the template?  

---

### Answer by username_2 on 2023-06-22. Score: 1

By the way if you want to use yaml for your cloud-init template you can achieve what you want using indent function
ssh_keys:
  rsa_private: |
    ${indent(4, "${private_key}")}

Make sure to adjust the indented spaces as per your template.


---

