Terraformでもいつでも最新AMIからEC2を起動したい

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

TerraformでAWS providerを利用しているとAMI IDをハードコードしなければならない場面があります。その都度AMCから「最新のAMI IDなんだっけ」と探さなければならず地味に面倒でした。CloudFormationの場合はLambda-Backed Custom Resourcesを使うことによって、EC2インスタンス起動時にLambda関数を起動し最新AMI IDを取得するという手法でこの問題を解決できます。詳細は以下のエントリを参照してください。

CloudFormationうらやましい、でもAMI ID取得するのにLambda関数起動するのオーバースペックな気もする、などとアンビバレンツな乙女心を抱いておりましたが、Terraformでもv.0.7.0から同等の機能が取り込まれました。Data sourcesという機能です。

「Data sources」とは何か

この機能は#6598のPRで取り込まれました。すごい。ドキュメントから要点を引用すると以下の通りです。

Data sources allow data to be fetched or computed for use elsewhere in Terraform configuration. Use of data sources allows a Terraform configuration to build on information defined outside of Terraform, or defined by another separate Terraform configuration. 略 For example, a data source may retrieve artifact information from Atlas, configuration information from Consul, or look up a pre-existing AWS resource by filtering on its attributes and tags.

つまりTerraform外から情報を取得し、Terraform内でその情報を参照できる機能です。この機能を使うことにより、例えば冒頭で説明したAMI IDハードコード業から開放されるわけです。

現在対応しているData sourceはこちらを参照してください。以下の4つがあるようです。

  • aws_ami
  • aws_availability_zones
  • aws_iam_policy_document
  • aws_s3_bucket_object

今回はaws_amiをご紹介します。

それでは早速使ってみましょう。

v0.7.0-rc2をコンパイルする

まずはTerraformを用意します。現時点(2016/06/17)ではまだv0.7.0がリリースされていません。幸いRC版(v0.7.0-rc2)が出ているのでそれを利用します。v0.7.0-rc2のcommit hash値は「46a0709bba004d8b6e0eedad411270b3ae135a9e」です。ソースからコンパイルする方法はREADMEを参照してください。以下はすでに GOPATH の設定が完了している場合の作業手順です。

$ cd $GOPATH/src/github.com/hashicorp/terraform
$ git fetch origin
$ git checkout 46a0709bba004d8b6e0eedad411270b3ae135a9e
$ git log -1
commit 46a0709bba004d8b6e0eedad411270b3ae135a9e
Author: Paul Hinze <phinze@phinze.com>
Date:   Sun Jun 12 19:07:52 2016 +0000

    v0.7.0-rc2
$ make dev
$ $GOPATH/bin/terraform version
Terraform v0.7.0-rc2 (46a0709bba004d8b6e0eedad411270b3ae135a9e)

aws_amiを使う

サンプルコードをGitHubに上げておきました。以下のコマンドで実行可能です。

$ git clone https://github.com/knakayama/terraform-data-sources-demo.git
$ cd terraform-data-sources-demo
$ ssh-keygen -f site_key -N ''
$ $GOPATH/bin/terraform plan
$ $GOPATH/bin/terraform apply

コードの解説

肝となるコードが以下です。

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }

  filter {
    name   = "name"
    values = ["amzn-ami-hvm-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "block-device-mapping.volume-type"
    values = ["gp2"]
  }
}

awscliのdescribe-imagesコマンドや各種AWS SDKの同等機能を利用したことがある方なら見ただけで内容が把握できるかと思います。それぞれのargumentを以下に解説します。

  • most_recent

複数の結果が返った時に最新のAMIを返します。

  • owners

AMIの所有者情報に基づき検索条件を絞ります。今回AWS公式AMIを利用するので amazon を指定しています。

  • filter

AMIの検索条件を絞ります。設定の記述方法はawscliのdescribe-images用ドキュメントを参照してください。

aws_instance リソースにこの出力結果を指定します。該当箇所をハイライトします。

resource "aws_instance" "ec2" {
  ami                         = "${data.aws_ami.amazon_linux.id}"
  instance_type               = "${var.instance_type}"
  vpc_security_group_ids      = ["${aws_security_group.ec2.id}"]
  subnet_id                   = "${aws_subnet.public.id}"
  key_name                    = "${aws_key_pair.site_key.key_name}"
  associate_public_ip_address = true

  root_block_device {
    volume_type = "gp2"
    volume_size = 8
  }
}

実行結果

意図した動作をするのでしょうか。実行してみましょう。

$ $GOPATH/bin/terraform apply
<snip>
State path: terraform.tfstate

Outputs:

latest_ami_id = ami-6154bb00
public_ip = 54.199.234.255

outputs.tf でattributeを参照しているため apply サブコマンド実行後、その値が出力されています。

output "latest_ami_id" {
  value = "${data.aws_ami.amazon_linux.id}"
}

output "public_ip" {
  value = "${aws_instance.ec2.public_ip}"
}

ami-6154bb00 は2016/06/17現在、東京リージョンの最新AmazonLinux AMI IDです。インスタンス上でも確認してみましょう。

$ ssh -i site_key ec2-user@54.199.234.255
$ curl -s http://169.254.169.254/latest/meta-data/ami-id
ami-6154bb00

まとめ

いかがだったでしょうか。

Data sourcesで夢がひろがりんぐですね。

本エントリがみなさんの参考になれば幸いです。