如何自动将Packer输出的AMI ID链接到Terraform变量?

23
我正在使用带有Ansible provisioner的Packer构建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方法。在此解决方案中,我从packer输出中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}
4个回答

32

你应该考虑使用Terraform的Data Source来获取aws_ami。通过这种方式,您可以依赖于您在创建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}"
}

这样做的另一个好处是,您可以使用相同的配置在多个地区部署,而无需使用变量映射!


3
我发现在这个工作流中有一个有用的做法是将git提交哈希值作为标签包含进来。这样,如果terraform模板可以使用相对路径引用到packer构建以获取提交哈希值,那么就有一种方法来检索与版本控制相关联的AMI。例如:export PKR_VAR_commit_hash="$(git rev-parse HEAD)" - openCivilisation
谢谢。我通过使用名称过滤器完成了它。运行顺畅。 - ashraf minhaj

7
Hashicorp官方推荐的方法是使用他们的产品Atlas作为两者之间的“中间人”。您可以使用Packer中的Atlas后处理器记录工件(在您的情况下为AMI ID),然后使用Terraform中的atlas_artifact资源再次读取ID以供Terraform使用。
在这种情况下,您将从资源中获取ID,而不是使用变量传递它们。
除了Atlas之外,其他选项相当有限,在某些情况下也很麻烦。
如果您想完全不使用任何外部服务,则可以尝试使用本地shell后处理器作为运行工件上的本地命令的方式,或者您可以使用机器可读输出提取AMI ID并将其写入Terraform的变量文件中。
另外一个选项是编写自己的后处理插件,与您已经使用的某些软件进行交互,作为Atlas的替代方案。例如,我和一些同事一起编写了一个后处理器,将构建工件记录为元数据在Buildkite中, 然后我们使用Buildkite API检索这些工件。这需要使用Go编写自定义代码。
在撰写本文时,Terraform版本0.7仍在开发中,但计划包括一项新功能,允许直接查询EC2 API以获取AMI,如果它确实在0.7中推出,这将允许通过Packer对AMI进行标记,并使用这些标记直接从EC2中找到它。这使用EC2本身作为“中间人”,这或许不那么麻烦,因为EC2已经作为存储AMI的方式参与其中了。

这里指向Hashicorp上的Atlas的链接无法使用。 - openCivilisation
2
HashiCorp的“atlas”服务在2017年被EOL,我写这篇评论时是有效的,但今天第一段不再有效。 - Martin Atkins
1
请编辑您的回复以反映该更新-以帮助其他人节省时间,并防止不必要的负评。 - Stephen

2
这是我所采用的方法:
  1. 封装packer调用并通过解析输出获取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设置中可用。

其次,创建一个运行Packer的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 $1')

# 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}"
  ...
}

0
我采用了最简单的方法,给我的AMI命名,并在Terraform中查找该名称。 image.pkr.hcl 文件 -
packer {
  required_plugins {
    amazon = {
      version = ">= 0.0.2"
      source  = "github.com/hashicorp/amazon"
    }
  }
}

source "amazon-ebs" "ubuntu" {
  ami_name      = "your_ami_name"
  instance_type = "t2.micro"
  region        = "${var.aws_region}"
  source_ami_filter {
    filters = {
      name                = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    most_recent = true
    owners      = "${var.image_owners}"
  }
  ssh_username = "ubuntu"
}

build {
  name = "build-image"
  sources = [
    "source.amazon-ebs.ubuntu"
  ] 
#.....
}

ec2.tf 文件是 -

data "aws_ami" "ami" {
  most_recent = true
  owners = ["self"]
  filter {
    name = "name"
    values = ["your_ami_name"]
  }
}

output "ami_id" {
  value = data.aws_ami.ami.id
}

resource "aws_instance" "myEc2" {
  ami = data.aws_ami.ami.id
  instance_type = "t2.micro"
  key_name      = "----key"
  vpc_security_group_ids = [
    "launch-wizard-18"
  ]
    user_data = <<-EOL
      #!/bin/bash -xe
      echo "if done file exists it went ok"
      touch done.txt

      EOL

  tags = {
    Name = "child-of-your-ami"
  }
}

希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接