如何自动将打包器输出ami id链接到terraform变量?

时间:2016-05-21 00:19:40

标签: bash amazon-web-services packer terraform

我正在使用带有ansible配置器的打包器来构建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,region等。这里的问题是我不想手动或多次指定它们。对于区域(我事先知道),因为我可以在两种情况下使用环境变量,所以很容易,但是输出ami呢?是否有内置的方式来链接这些产品或某些不那么hacky方法来做它?

对于可能感兴趣的人,

编辑: Hacky方法。在这个解决方案中,我是grep aws region& ami来自packer输出并使用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}

3 个答案:

答案 0 :(得分:18)

您应该考虑将{Terraform的数据源用于aws_ami。这样,您可以依赖在创建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}"
}

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

答案 1 :(得分:7)

"官方" Hashicorp推荐的方式是使用他们的产品Atlas作为"中间人"两者之间。您使用the Atlas post-processor in Packer来记录工件(AMI ID,在您的情况下),然后使用the atlas_artifact resource in Terraform再次读取ID以便在Terraform中使用。

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

除了Atlas之外,其他选择相当有限,在某些情况下是hacky。

如果您想在没有任何外部服务的情况下进行此操作,那么您可以尝试使用local shell post-processor作为在工件上运行本地命令的方法,或者您可以使用the machine-readable output来提取AMI将它们写入Terraform的变量文件中。

另一个选择是编写自己的后处理器插件,与您已经使用的某些软件交互,作为Atlas的替代品。例如,我的一些同事写了a post-processor to record artifacts as metadata in Buildkite,然后我们使用Buildkite API检索。这需要在Go中编写自定义代码。

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

答案 2 :(得分:0)

这是我使用的方法:

  1. 通过解析输出
  2. 来包装打包器并获取AMI
  3. 使用已解析的AMI创建Terraform文件,该文件将值作为变量提供
  4. 它类似于编辑答案中的版本。更详细地说,它看起来像这样:

    首先,创建一个名为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文件,这样可以将装箱中的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}"
      ...
    }