使用 Terraform 模板生成 cloud-init yaml:模板变量不保留yaml上下文/空格

67次阅读
没有评论

问题描述

在使用 Terraform 时,有一个关于使用模板生成 cloud-init 文件的问题。Terraform 提供了一个函数 templatefile,可以将 Terraform 本地或外部获取的变量应用于 Jinja2 模板。用户尝试根据 Terraform 文档中的示例,使用这些模板生成包含 Jinja2 的 cloud-init 文件。

用户的模板示例如下:

ssh_keys:
  ecdsa_private: "${ssh_ecdsa_key_private}"

然后,用户在 Terraform 中设置这些变量:

data "template_file" "user_data" {
  template = templatefile(
    "cloud/cloud.cfg",
    {
      ssh_ecdsa_key_private  = file("./ssh/acme-bitbucket-id_ecdsa")
    }
  )
}

然而,问题出现在 cloud-init 文件的 yaml 格式上。

用户遇到的错误消息大致如下:

in "<unicode string>", line 22, column 1:    "    ^could not find expected ':'  in "<unicode string>", line 24, column 8:          "-----BEGIN OPENSSH PRIVATE KEY-----           ^"...

用户认为问题可能是文件的缩进格式不正确,导致 yaml 解析出错。

解决方案

以下是解决这个问题的几种方案,根据情况选择适合的方法。

方案1:使用 HDL 对象处理模板变量

在 Terraform 中,你可以将模板变量作为 HDL 对象(哈希、字典、列表等)进行处理,这有助于避免一些格式化问题。下面是一个示例:

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")
      }
    }
  )
}

在这个示例中,我们将 ssh_keys 作为一个字典对象处理,其中包含私钥和公钥的文件内容。这样可以更好地维护格式。

方案2:使用 yamlencode 函数

Terraform 提供了 yamlencode 函数,可以将变量编码为 yaml 格式,确保生成的 cloud-init 文件保留正确的格式。以下是使用 yamlencode 函数的示例:

data "template_file" "user_data" {
  template = templatefile(
    "cloud/cloud.cfg",
    {
      ssh_keys = {
        ssh_ecdsa_key_private  = file("./ssh/acme-bitbucket-id_ecdsa")
      }
    }
  )
}

resource "null_resource" "example" {
  triggers = {
    user_data = yamlencode(data.template_file.user_data.rendered)
  }
}

在这个示例中,我们使用了一个 null_resource 来触发 yamlencode 函数并将生成的 yaml 内容存储在触发器中。这可以确保生成的 cloud-init 文件在 yaml 上下文中保持正确的格式。

请根据实际情况选择适合你需求的方案。如果你对 yaml 的格式有特殊要求,建议使用方案2,因为它可以更好地控制生成的文件格式。

正文完