AWSのNATインスタンス用AMIを取得するTerraformモジュールを作ってみた

TerraformでAmazon LinuxのNATインスタンス用AMIを取得するためのモジュールを作成してみました。

https://github.com/atsaki/tf_aws_nat_ami

# 使い方

リージョンやバージョン等のパラメータを指定してNATインスタンス用AMIのIDを取得することができます。

```
module "nat_instance_ami" {
  source = "github.com/atsaki/tf_aws_nat_ami"
  region = "eu-central-1
  version = "2015.03.0"
  virttype = "pv"
  volumetype = "ebs"
}
```
regionのみ必須で、他は省略可能です。

* region : （例：eu-central-1）
* version : （例：2015.03.0、デフォルト：2015.03.0）
* virttype : hvm or pv （デフォルト：hvm）
* volumetype : gp2 or ebs (デフォルト：gp2）

実際のtfファイル、tfvarsファイルは以下のようになります。（NATを行うためにはroute tableの設定などが必要ですが今回は割愛します。）

```main.tf
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {}

provider "aws" {
  access_key = "${var.aws_access_key}"
  secret_key = "${var.aws_secret_key}"
  region = "${var.region}"
}

module "nat_instance_ami" {
  source = "github.com/atsaki/tf_aws_nat_ami"
  region = "${var.region}"
  virttype = "pv"
  volumetype = "ebs"
}

resource "aws_instance" "nat_instance" {
  ami = "${module.nat_instance_ami.ami_id}"
  instance_type = "t1.micro"
  tags {
    Name = "nat_instance"
  }
}
```

```terraform.tfvars
aws_access_key = "YOUR_ACCESS_KEY"
aws_secret_key = "YOUR_SECRET_KEY"
region = "YOUR_REGION"
```

tfファイルができたら```terraform get``` コマンドでモジュールをダウンロードします。

```bash
$ terraform get
```

この後は、通常通り```terraform plan``` や ```terraform apply``` を実行可能です。

# 仕組み

このモジュールは[terraform-community-modules](https://github.com/terraform-community-modules)の[tf_aws_fedora_ami](https://github.com/terraform-community-modules/tf_aws_fedora_ami)、[tf_aws_ubuntu_ami](https://github.com/terraform-community-modules/tf_aws_ubuntu_ami)を参考に作成しました。

何れも仕組みとしては同じで前もってスクリプトでAMIのリストを取得しTerraformの変数として保存しておき、モジュールのパラメータを使ってlookupしています。

今回作成したモジュールでは[AWS SDK for Ruby](http://aws.amazon.com/jp/sdk-for-ruby/)を使って各リージョンのAMIのリストを取得し、名前が```amzn-ami-vpc-nat``` から始まるAMIの情報を```variables.tf.json``` に保存しています。

```rb:getvariables.rb
#!/usr/bin/env ruby

require 'aws-sdk'
require 'json'

regions = Aws::EC2::Client.new(region: 'us-east-1')
    .describe_regions.regions.map { |r| r.region_name }

data = {}
regions.each do |region|
    images = Aws::EC2::Client.new(region: region).describe_images(
        owners: ['amazon'],
        filters: [{ name: 'image-type', values: ['machine'] }]
    ).images.select { |image|
        !image.name.nil? && image.name.start_with?("amzn-ami-vpc-nat")
    }
    data.merge!(Hash[images.map {|i| ["#{region},#{i.name}", i.image_id]}])
end

puts JSON.pretty_generate({ 
    variable: {
        amis: {
            default: data
        }
    }
})
```

```variables.tf.json``` の中は以下のようになっており、リージョン名とAMI名をキー、AMI idをバリューとしてAMIのリストが格納されています。

```json:variables.tf.json
{
  "variable": {
    "amis": {
      "default": {
        "eu-central-1,amzn-ami-vpc-nat-hvm-2015.03.0.x86_64-ebs": "ami-1e073a03",
        "eu-central-1,amzn-ami-vpc-nat-hvm-2014.03.2.x86_64-ebs": "ami-204c7a3d",
        "eu-central-1,amzn-ami-vpc-nat-pv-2015.03.0.x86_64-ebs": "ami-3604392b",
        "eu-central-1,amzn-ami-vpc-nat-hvm-2015.03.0.x86_64-gp2": "ami-46073a5b",
      〜〜〜省略〜〜〜
      }
    }
  }
}
```

```main.tf``` では```lookup``` 関数を使ってモジュールのパラメータで指定された条件と合致するAMIを抽出しIDを出力しています。
（```lookup``` や```format``` については[Terraformのドキュメント](https://www.terraform.io/docs/configuration/interpolation.html)を参照）


```main.tf
variable "region" {}
variable "version" {
  default = "2015.03.0"
}
variable "virttype" {
  default = "hvm"
}
variable "volumetype" {
  default = "gp2"
}

output "ami_id" {
  value = "${lookup(var.amis, format(\"%s,amzn-ami-vpc-nat-%s-%s.x86_64-%s\", var.region, var.virttype, var.version, var.volumetype))}"
}
```

# 参考

* [terraform-community-modules](https://github.com/terraform-community-modules)
* [NAT インスタンス - Amazon Virtual Private Cloud](http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_NAT_Instance.html)
* [Modules - Terraform by HashiCorp](https://www.terraform.io/docs/modules/index.html)