Terraform - AWS NATインスタンス環境構築

# 使用モジュール

VPC環境作成用と、NATインスタンス作成用で以下2つのモジュールを使用する

- [aws-ia/vpc/aws](https://github.com/aws-ia/terraform-aws-vpc)
- [int128/nat-instance/aws](https://github.com/int128/terraform-aws-nat-instance#getting-started)

# アーキテクチャ

[int128/nat-instance/aws](https://github.com/int128/terraform-aws-nat-instance#getting-started)のReadmeにある以下の構成から、  
各Availability ZoneにそれぞれNATインスタンスを配置する構成に変更する

![2023-06-03-14-43-33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/90d715ab-2f22-e5de-9e15-b3b6004cc756.png)


# 実装

```terraform
locals {
  name_prefix = "nat-instance-test"
}

# VPC環境構築
module "vpc" {
  source  = "aws-ia/vpc/aws"
  version = ">= 4.2.0"

  name       = "${local.name_prefix}-vpc"
  cidr_block = "10.0.0.0/16"
  az_count   = 2

  subnets = {
    public = {
      name_prefix               = "${local.name_prefix}-public-subnet"
      cidrs                     = ["10.0.1.0/24", "10.0.2.0/24"]
      nat_gateway_configuration = "none"
    }
    private = {
      name_prefix = "${local.name_prefix}-private-subnet"
      cidrs       = ["10.0.11.0/24", "10.0.12.0/24"]
    }
  }
}

# NATインスタンス作成
module "nat" {
  source  = "int128/nat-instance/aws"
  version = ">= 2.1.0"

  # AZごとNATインスタンスを作成する
  for_each = toset(module.vpc.azs) # each.key = az

  name                        = "${local.name_prefix}-${each.key}"
  vpc_id                      = module.vpc.vpc_attributes.id
  public_subnet               = module.vpc.public_subnet_attributes_by_az[each.key].id
  private_subnets_cidr_blocks = [module.vpc.private_subnet_attributes_by_az["private/${each.key}"].cidr_block]
  private_route_table_ids     = [module.vpc.rt_attributes_by_type_by_az.private["private/${each.key}"].id]
}

# EIP割り当て
resource "aws_eip" "nat_eip" {

  # NATインスタンスごとにEIPを割り当てる
  for_each = module.nat # each.key = az

  network_interface = each.value.eni_id
  tags = {
    "Name" = "${local.name_prefix}-nat-instance-eip-${each.key}"
  }
}

output "aws_vpc_eip_public_ip" {
  # 割り当てた各EIPをoutputする
  value = [ for value in aws_eip.nat_eip : value.public_ip ]
  sensitive = true
}
```

## objectの参照

[aws-ia/vpc/aws](https://github.com/aws-ia/terraform-aws-vpc)のoutput `private_subnet_attributes_by_az` は以下形式になっている

```terraform
{
  "private_subnet_attributes_by_az": {
    "sensitive": true,
    "type": [
      "object",
      {
        "private/ap-northeast-1a": [
          "object",
          {
            "arn": "string",
            "assign_ipv6_address_on_creation": "bool",
            "availability_zone": "string",
            "availability_zone_id": "string",
            "cidr_block": "string",
            "customer_owned_ipv4_pool": "string",
            "enable_dns64": "bool",
            "enable_lni_at_device_index": "number",
            "enable_resource_name_dns_a_record_on_launch": "bool",
            "enable_resource_name_dns_aaaa_record_on_launch": "bool",
            "id": "string",
            "ipv6_cidr_block": "string",
            "ipv6_cidr_block_association_id": "string",
            "ipv6_native": "bool",
            "map_customer_owned_ip_on_launch": "bool",
            "map_public_ip_on_launch": "bool",
            "outpost_arn": "string",
            "owner_id": "string",
            "private_dns_hostname_type_on_launch": "string",
            "tags": [
              "map",
              "string"
            ],
            "tags_all": [
              "map",
              "string"
            ],
            "timeouts": [
              "object",
              {
                "create": "string",
                "delete": "string"
              }
            ],
            "vpc_id": "string"
          }
        ]
      }
    ]
  }
}
```

上記実装では `aws-ia/vpc/aws`モジュールによってリソースが配置されるAZは特定できないため、  
VPC環境作成用モジュールのoutputをkeyにobject参照を行っている

```terraform
# private_subnets_cidr_blocks = [module.vpc.private_subnet_attributes_by_az.private/ap-northeast-1a.cidr_block]
private_subnets_cidr_blocks = [module.vpc.private_subnet_attributes_by_az["private/${each.key}"].cidr_block]
```

## moduleにfor_eachを使用した際のoutput

moduleにfor_eachを使用した場合、outputはfor_eachのkeyをkeyとしたobjectになる

以下のように`int128/nat-instance/aws` moduleにfor_eachを使用し、  
`module.vpc.azs = ["ap-northeast-1a", "ap-northeast-1c"]` だった場合、

```terraform
module "nat" {
  source  = "int128/nat-instance/aws"
  version = ">= 2.1.0"

  # AZごとNATインスタンスを作成する
  for_each = toset(module.vpc.azs) # each.key = az

  ...
}
```

output(`module.nat`)は、以下のような形式になる

```terraform
{
  "ap-northeast-1a": {
    "eni_id": "12345678",
    "eni_private_ip": "1.1.1.1",
    "iam_role_name": "role_name",
    "sg_id": "12345678"
  },
  "ap-northeast-1c": {
    "eni_id": "12345678",
    "eni_private_ip": "1.1.1.1",
    "iam_role_name": "role_name",
    "sg_id": "12345678"
  }
}
```