さっそく VPC NAT Gateway を使ってみた（terraform と awscli で）

[【新機能】マネージドNATゲートウェイが利用可能に](http://aws.typepad.com/aws_japan/2015/12/managednat.html)

という発表があったので、さっそく使ってみました。

## Terraform でいろいろ作る

Terraform で VPC やら EC2 インスタンスやらを作ります。ちょっと長いです。

```:main.tf
////////////////////////////////////////////////////////////////////////////////
/// Variable

variable "ami_id" {
    default = "ami-b80b6db8" // CentOS 7 x86_64 (2014_09_29) EBS
}

////////////////////////////////////////////////////////////////////////////////
/// VPC

/// VPC

resource "aws_vpc" "test" {
    cidr_block = "10.9.0.0/16"
    tags { Name = "test" }
}

/// Internet Gateway

resource "aws_internet_gateway" "test" {
    vpc_id = "${aws_vpc.test.id}"
    tags { Name = "test-igw" }
}

/// Subnet

resource "aws_subnet" "front" {
    vpc_id = "${aws_vpc.test.id}"
    availability_zone = "ap-northeast-1a"
    cidr_block = "10.9.0.0/24"
    map_public_ip_on_launch = true
    tags { Name = "test-front" }
}

resource "aws_subnet" "back" {
    vpc_id = "${aws_vpc.test.id}"
    availability_zone = "ap-northeast-1a"
    cidr_block = "10.9.1.0/24"
    map_public_ip_on_launch = false
    tags { Name = "test-back" }
}

/// Route Table

resource "aws_route_table" "front" {
    vpc_id = "${aws_vpc.test.id}"
    tags { Name = "test-front" }
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.test.id}"
    }
}

resource "aws_route_table" "back" {
    vpc_id = "${aws_vpc.test.id}"
    tags { Name = "test-back" }
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.test.id}"
    }
}

resource "aws_route_table_association" "front" {
    subnet_id = "${aws_subnet.front.id}"
    route_table_id = "${aws_route_table.front.id}"
}

resource "aws_route_table_association" "back" {
    subnet_id = "${aws_subnet.back.id}"
    route_table_id = "${aws_route_table.back.id}"
}

/// Security Group

resource "aws_security_group" "main" {
    vpc_id = "${aws_vpc.test.id}"
    name = "test-main"
    description = "test-main"
    tags { Name = "test-main" }
    ingress {
        protocol = -1
        from_port = 0
        to_port = 0
        self = true
    }
    egress {
        protocol = -1
        from_port = 0
        to_port = 0
        cidr_blocks = ["0.0.0.0/0"]
    }
}

resource "aws_security_group" "devops" {
    vpc_id = "${aws_vpc.test.id}"
    name = "test-devops"
    description = "test-devops"
    tags { Name = "test-devops" }
    ingress {
        protocol = "tcp"
        from_port = 22
        to_port = 22
        cidr_blocks = ["192.0.2.123/32"]
    }
    egress {
        protocol = -1
        from_port = 0
        to_port = 0
        cidr_blocks = ["0.0.0.0/0"]
    }
}

/// Elastic IP

resource "aws_eip" "nat" {
    vpc = true
}

////////////////////////////////////////////////////////////////////////////////
/// EC2

resource "aws_instance" "bastion" {
    ami = "${var.ami_id}"
    instance_type = "t2.nano"
    key_name = "oreore"
    vpc_security_group_ids = [
        "${aws_security_group.main.id}",
        "${aws_security_group.devops.id}"
    ]
    subnet_id = "${aws_subnet.front.id}"
    associate_public_ip_address = true
    root_block_device = {
      volume_type = "gp2"
      delete_on_termination = true
    }
    tags {
        Name = "test-bastion"
    }
    user_data = <<EOS
        #cloud-config
        hostname: "bastion"
        timezone: "Asia/Tokyo"
EOS
}

resource "aws_instance" "server" {
    ami = "${var.ami_id}"
    instance_type = "t2.nano"
    key_name = "oreore"
    vpc_security_group_ids = [
        "${aws_security_group.main.id}"
    ]
    subnet_id = "${aws_subnet.back.id}"
    associate_public_ip_address = false
    private_ip = "10.9.1.10"
    root_block_device = {
      volume_type = "gp2"
      delete_on_termination = true
    }
    tags {
        Name = "test-server"
    }
    user_data = <<EOS
        #cloud-config
        hostname: "server"
        timezone: "Asia/Tokyo"
EOS
}

////////////////////////////////////////////////////////////////////////////////
/// Output

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

output "subnet.front.id" {
    value = "${aws_subnet.front.id}"
}

output "route_table.back.id" {
    value = "${aws_route_table.back.id}"
}

output "eip.nat.id" {
    value = "${aws_eip.nat.id}"
}
```

インスタンスを２つ立ち上げています。

**server** インスタンスは InternetGateway が無いサブネットに属していて、PublicIP もないため、直接外部と通信できません。

**bastion** インスタンスはいわゆる中継サーバで、InternetGatewayのあるサブネットに属していて、PublicIP も付いています。server インスタンスに SSH でログインするためにはこのインスタンスを経由する必要があります。

実行します。

```sh
terraform plan
terraform apply
```

`output` で後の作業で必要になる項目を出力させているので、覚えておきます。

```
 :

Outputs:

  bastion.public_ip   = 52.12.34.56
  eip.nat.id          = eipalloc-12345678
  route_table.back.id = rtb-12345678
  subnet.front.id     = subnet-12345678
```

## 直接インターネットにでれないことを確認

bastion インスタンス経由で server インスタンスにログインします。

```sh
ssh centos@10.9.1.10 -o "ProxyCommand ssh centos@52.12.34.56 -W %h:%p 2>/dev/null"
```

curl で適当などこかのサイトにアクセスします。

```sh
curl -I qiita.com -m 3
```

server インスタンスは直接インターネットには出られないのでタイムアウトになります。

```sh
curl: (28) Connection timed out after 3153 milliseconds
```

## VPC NAT Gateway を作る

Terraform はさすがにまだ対応していないので awscli で NAT Gateway を作ります。

ます awscli をアップデートします。

```sh
sudo pip install awscli --upgrade
```

最新版なら NAT Gateway に対応しています。

```sh
aws ec2 help | grep nat-gateway
       o create-nat-gateway
       o delete-nat-gateway
       o describe-nat-gateways
```

NAT Gateway を作ります。サブネットと Elastic IP を指定する必要があります。

```sh
aws ec2 create-nat-gateway --subnet-id "subnet-12345678" --allocation-id "eipalloc-12345678"
```

次のように結果が表示されました。

```json
{
    "NatGateway": {
        "NatGatewayAddresses": [
            {
                "AllocationId": "eipalloc-12345678"
            }
        ],
        "VpcId": "vpc-12345678",
        "State": "pending",
        "NatGatewayId": "nat-1234567890abcdef",
        "SubnetId": "subnet-12345678",
        "CreateTime": "2015-12-18T02:26:01.524Z"
    }
}
```

バックエンドのサブネットの RouteTable のデフォルトゲートウェイを NAT Gateway に変更します。

```sh
aws ec2 replace-route \
  --route-table-id "rtb-12345678" \
  --destination-cidr-block "0.0.0.0/0" \
  --nat-gateway-id "nat-1234567890abcdef"
```

もう一度 server インスタンスにログインして curl してみると・・・

```sh
curl -I qiita.com -m 3
```

インターネット経由のアクセスができるようになりました！！！

```
HTTP/1.1 301 Moved Permanently
 :
```

## 全部削除する

もういらないので削除します。

NAT Gateway を削除します。

```sh
aws ec2 delete-nat-gateway --nat-gateway-id "nat-1234567890abcdef"
```

削除には少し時間がかかります。下記のように NAT Gateway の State を表示して・・・

```sh
aws ec2 describe-nat-gateways --nat-gateway-id "nat-1234567890abcdef" \
  --query 'NatGateways[0].State' --output "text"
```

`deleting` だと削除中です。これが `deleted` になれば削除完了です。

NAT Gateway の削除が完了したら Terraform で作ったもろもろも削除します。

```
terraform plan -destroy
terraform destroy -force
```

おしまい。