【问题标题】:Terraform extensible user_data in aws_instanceaws_instance 中的 Terraform 可扩展 user_data
【发布时间】:2021-12-10 10:15:33
【问题描述】:

早安,

我们的团队使用了一个模块,该模块在 user_data 中创建具有标准配置的 Linux 实例,定义如下。

resource "aws_instance" "this" {
...
user_data = templatefile("${path.module}/user_data.tp", { hostname = upper("${local.prefix}${count.index + 1}"), domain = local.domain })
...
}

user_data.tp 的内容:

#cloud-config
repo_update: true
repo_upgrade: all

preserve_hostname: false
hostname: ${hostname}
fqdn: ${hostname}.${domain}
manage_etc_hosts: false

runcmd:
  - 'echo "preserve_hostname: true" >> /etc/cloud/cloud.cfg.d/99_hostname.cfg'

修改此模块的最佳方法是什么,以便始终执行 user_data.tp 的内容,并且可选地可以传递另一个块来安装某些包或执行某些 shell 脚本?

我假设它涉及使用 cloudinit_config 和多部分 mime 配置,但如果有任何建议,我将不胜感激。

谢谢。

【问题讨论】:

  • 总是执行是什么意思:每次应用,每次重启,还是完全其他的?另外,也许你可以举一个关于“额外”位的例子。您已经可以运行任意命令,例如在 user_data 中安装软件。
  • @theherk,很抱歉有歧义。我希望 user_data 仅在初始配置时执行。我希望能够修改模块,以便人们可以调用它并可选地传递另一个块以附加到 user_data。上面的 user_data.tp 需要对所有实例都是标准的,但是用户可能想要安装特定的包并在他们的实例上执行 bash 脚本。
  • @Mark,我想这就是我需要的。这仍然是最好的方法吗?
  • @Kimmel 是的,如果我知道更好的方法我会发布它。

标签: terraform terraform-provider-aws


【解决方案1】:

由于您展示了cloud-config 模板,我假设您正在为启动时运行cloud-init 的AMI 准备user_data。这意味着这可能更像是一个 cloud-init 问题而不是 Terraform 问题,但我知道您还想知道如何将特定于 cloud-init 的答案转换为可行的 Terraform 配置。

User-data Formats 文档描述了格式化 user_data 以供 cloud-init 使用的各种可能方式。您在问题中提到了多部分 MIME,如果您希望 cloud-init 分别解释两个有效负载,而不是作为单个工件,这可能是一个可行的答案。 cloud-init 文档讨论了工具 make-mime,但 Terraform 的等价物是 the cloudinit_config data source 属于 the hashicorp/cloudinit provider

variable "extra_cloudinit" {
  type = object({
    content_type = string
    content      = string
  })

  # This makes the variable optional to set,
  # and var.extra_cloudinit will be null if not set.
  default = null
}

data "cloudinit_config" "user_data" {
  # set "count" to be whatever your aws_instance count is set to
  count = ...

  part {
    content_type = "text/cloud-config"
    content      = templatefile(
      "${path.module}/user_data.tp",
      {
        hostname = upper("${local.prefix}${count.index + 1}")
        domain = local.domain
      }
    )
  }

  dynamic "part" {
    # If var.extra_cloud_init is null then this
    # will produce a zero-element list, or otherwise
    # it'll produce a one-element list.
    for_each = var.extra_cloudinit[*]
    content {
      content_type = part.value.content_type
      content      = part.value.content

      # NOTE: should probably also set merge_type
      # here to tell cloud-init how to merge these
      # two:
      # https://cloudinit.readthedocs.io/en/latest/topics/merging.html
    }
  }
}

resource "aws_instance" "example" {
  count = length(data.cloudinit_config.user_data)

  # ...
  user_data = data.cloudinit_config.user_data[count.index].rendered
}

如果您希望额外的 cloud-init 配置总是以额外的 cloud-config YAML 值的形式出现,那么另一种方法是在 Terraform 中将两个数据结构合并在一起,然后 yamlencode 合并结果:

variable "extra_cloudinit" {
  type = any

  # This makes the variable optional to set,
  # and var.extra_cloudinit will be null if not set.
  default = {}

  validation {
    condition     = can(merge(var.extra_cloudinit, {}))
    error_message = "Must be an object to merge with the built-in cloud-init settings."
  }
}

locals {
  cloudinit_config = merge(
    var.extra_cloudinit,
    {
      repo_update  = true
      repo_upgrade = "all"
      # etc, etc
    },
  )
}

resource "aws_instance" "example" {
  count = length(data.cloudinit_config.user_data)

  # ...
  user_data = <<EOT
#!cloud-config
${yamlencode(local.cloudinit_config)}
EOT
}

这种方法的一个缺点是 Terraform 的 merge 函数始终只是浅合并,而 cloud-init 本身具有 various other merging options。但是,一个优点是生成的单个 YAML 文档通常比多部分 MIME 有效负载更简单,因此可能更容易检查 terraform plan 输出中的正确性。

【讨论】:

  • 一如既往的彻底回答。我非常感激。实际上,根据您去年年底提供的类似答案,我昨天选择了您的第一个选项。唯一真正的区别是我使用 concat 来组合我的静态和动态列表,所以我只需要一个动态的“部分”。我实际上更喜欢您的 yamlencode 选项,并且可能会走这条路。一个简单的问题 - 在我的“extra_cloudinit”变量中,我使用“[]”作为默认值而不是 null。这会改变行为吗?
  • 如果您将extra_cloudinit 声明为要连接的列表,那么[] 确实是一个很好的“什么都不做”默认值。我在这里使用了null,因为我将它声明为单个对象,因此表示它的缺失的合乎逻辑的方式是null,然后[*] 在需要时变成空列表。
猜你喜欢
  • 2019-06-28
  • 2019-01-16
  • 1970-01-01
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2021-03-10
  • 2017-09-24
  • 1970-01-01
相关资源
最近更新 更多