Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2023-06-05T15:42:52.433Z
Answers: Ok, I got this far in the docs as well -- but where is the `yamlencode` called from? Within the template?
Answers: ms-ati
Answers: CC BY-SA 4.0
Answers: 2022-03-18T06:47:55.990Z
Answers: 2022-03-18T06:47:55.990Z
Answers: Evan Carroll
Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2023-06-22T11:58:20.887Z
Answers: 2023-06-22T11:58:20.887Z
Answers: Trayan Azarov
Body: <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>
  
Tags: <terraform><cloud-init>
Title: Using a terraform template to produce a cloud-init yaml: template variables do not respect yaml context/whitespace
UserDisplayName: Evan Carroll
ThreadText: # 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](https://www.terraform.io/language/functions/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](https://learn.hashicorp.com/tutorials/terraform/cloud-init).
  
  People use these templates to generate cloud-init files with Jinja2 templates. Here is a snipped of the template,
  
  ```yaml
  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 "<unicode string>", line 22, column 1:
      "
      ^
  could not find expected ':'
    in "<unicode string>", 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,
  
  ```yaml
  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`](https://www.terraform.io/language/functions/yamlencode).
  
  There is a bit of documentation that covers this, over at [*"Generating JSON or YAML from a template"*](https://www.terraform.io/language/functions/templatefile#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](https://developer.hashicorp.com/terraform/language/functions/indent)
  
  ```yaml
  ssh_keys:
    rsa_private: |
      ${indent(4, "${private_key}")}
  
  ```
  
  Make sure to adjust the indented spaces as per your template.
  
  
  ---
  
  