如何自动将加壳器输出的 ami id 链接到 terraform 变量?

How can I chain packer output ami id to terraform variables automatically?

我正在使用加壳器和 ansible provisioner 构建 ami,并使用 terraform 以该 ami 作为源来设置基础设施 - 有点类似于这篇文章:http://www.paulstack.co.uk/blog/2016/01/02/building-an-elasticsearch-cluster-in-aws-with-packer-and-terraform

当命令 packer build pack.json 成功完成时,我得到以下格式的输出 ami id:

eu-central-1: ami-12345678

在我的 terraform 变量中 variables.tf 我需要指定源 ami id、区域等。这里的问题是我不想手动或多次指定它们。对于区域(我事先知道)这很容易,因为我可以在两种情况下使用环境变量,但是输出 ami 呢?是否有一种内置的方式来链接这些产品或一些不那么 hacky 的方法来做到这一点?

编辑: 适合任何可能感兴趣的人的 Hacky 方法。在这个解决方案中,我 grep 从打包程序输出中获取 aws 区域和 ami,并在 perl 中使用正则表达式将结果写入 terraform.tfvars 文件:

vars=$(pwd)"/terraform.tfvars"
packer build pack.json | \
    tee /dev/tty | \
    grep -E -o '\w{2}-\w+-\w{1}: ami-\w+' | \
    perl -ne '@parts = split /[:,\s]+/, $_; print "aws_amis." . $parts[0] ." = \"" . $parts[1] . "\"\n"' > ${vars}

Hashicorp 推荐的 "official" 方法是使用他们的产品 Atlas 作为两者之间的 "middleman"。您将使用 the Atlas post-processor in Packer to record the artifacts (AMI ids, in your case) and then use the atlas_artifact resource in Terraform 再次读回 id 以便在 Terraform 中使用。

在这种情况下,您将从资源中获取 ID,而不是使用变量传递它们。

除了 Atlas 之外,其他选项相当有限,在某些情况下还很老套。

如果你想在没有任何外部服务的情况下完成它,那么你可以尝试 local shell post-processor as a way to run a local command on your artifact, or you can use the machine-readable output 来提取 AMI id 并将它们写入 Terraform 的变量文件。

另一种选择是编写您自己的 post-处理器插件,与您已经使用的一些软件交互,作为 Atlas 的替代方案。例如,我和我的一些同事写了a post-processor to record artifacts as metadata in Buildkite, which we then subsequently retrieve using the Buildkite API。这需要在 Go 中编写自定义代码。

在撰写本文时,Terraform 版本 0.7 仍在开发中,但计划包含一项新功能,允许直接查询 EC2 API 的 AMI,这将(如果它确实登陆 0.7 ) 允许进一步选择使用 Packer 标记 AMI,然后使用这些标记直接从 EC2 找到它。这使用 EC2 本身作为 "middleman",这可能不那么尴尬,因为它已经作为 AMI 的存储涉及。

您应该考虑为 aws_ami 使用 Terraform 的 Data Source。这样,您就可以依赖在创建 AMI 时在其上设置的自定义标签(例如版本号或时间戳)。然后,在 Terraform 配置中,您可以简单地过滤此帐户和区域的可用 AMI,以获取您需要的 AMI ID。

https://www.terraform.io/docs/providers/aws/d/ami.html

data "aws_ami" "nat_ami" {
  most_recent = true
  executable_users = ["self"]
  filter {
    name = "owner-alias"
    values = ["amazon"]
  }
  filter {
    name = "name"
    values = ["amzn-ami-vpc-nat*"]
  }
  name_regex = "^myami-\d{3}"
  owners = ["self"]
}

注意:在上面的示例中(来自文档),过滤器的组合可能过多。您可能可以通过类似的方式过得很好:

data "aws_ami" "image" {
  most_recent = true
  owners = ["self"]
  filter {                       
    name = "tag:Application"     
    values = ["my-app-name"]
  }                              
}

output "ami_id" {
  value = "${data.aws_ami.image.id}"
}

这样做的另一个好处是您可以使用相同的配置部署到多个区域并且没有变量映射!

这是我使用的方法:

  1. 打包打包程序调用并通过解析输出获取 AMI
  2. 使用解析后的 AMI 创建 Terraform 文件,将值作为变量提供

它与编辑后的答案中的版本相似。更详细地说,它可以是这样的:

首先,创建一个名为 ami.tf.template 的文件:

# "ami.tf" was automatically generated from the template "ami.tf.template".
variable "ami" {
  default     = "${AMI_GENERATED_BY_PACKER}"
  description = "The latest AMI."
}

此模板将用于创建 ami.tf 文件,该文件使来自 packer 的 AMI 可用于您现有的 Terraform 设置。

其次,为 运行 加壳器创建一个 shell 包装器脚本。您可以使用以下想法:

# run packer (prints to stdout, but stores the output in a variable)
packer_out=$(packer build packer.json | tee /dev/tty)

# packer prints the id of the generated AMI in its last line
ami=$(echo "$packer_out" | tail -c 30 | perl -n -e'/: (ami-.+)$/ && print ')

# create the 'ami.tf' file from the template:
export AMI_GENERATED_BY_PACKER="$ami" && envsubst < ami.tf.template > ami.tf

脚本完成后,它会创建一个 ami.tf 文件,可能如下所示:

# "ami.tf" was automatically generated from the template "ami.tf.template".
variable "ami" {
  default     = "ami-aa92a441"
  description = "The latest AMI."
}

最后,将该文件放在您现有的 Terraform 设置旁边。然后您可以像这样访问 AMI:

resource "aws_launch_configuration" "foo" {
  image_id = "${var.ami}"
  ...
}