terraformで作るVPCテンプレート

Plannning-Venue Advent Calendar 2021の10日目です。

https://adventar.org/calendars/7174

今回はvpcをterraform化したものについて書いていきます。
terraform化をしておくことで、検証環境をかんたんに構築することが出来ます。
terraformユーザーはそれぞれ自分好みのテンプレートを持っていると思います。
とりあえずこれはあくまで一例ということで読んでいただけたら幸いです。

書き方は色々ありますが、以下に記載していくものはなるべく行数を減らしてworkspace化しているものとなります。

workspaceについてはコチラ

https://www.terraform.io/language/state/workspaces

```:version
Terraform v1.1.0
on darwin_amd64

Your version of Terraform is out of date! The latest version
is 1.1.2. You can update by downloading from https://www.terraform.io/downloads.html
```

構成はこんな感じです。

```
./
├── internet_gateway.tf
├── main.tf
├── nat_gateway.tf
├── route_table.tf
├── routing.tf
├── subnet.tf
└── variables.tf
```

lookup関数なども使用しているので、terraform初学者の方は参考になるかもしれません。
では行きましょう。

```internet_gateway.tf
# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = lookup(var.vpc_name, "${terraform.workspace}.vpc_name")
  }
}
```

```main.tf
# VPC
resource "aws_vpc" "main" {
  cidr_block           = lookup(var.vpc, "${terraform.workspace}.vpc")
  enable_dns_hostnames = true
  tags = {
    Name   = lookup(var.vpc_name, "${terraform.workspace}.vpc_name")
    Region = lookup(var.region, "${terraform.workspace}.region")
  }
}
```

```nat_gateway.tf
# Elastic IP
resource "aws_eip" "nat_1a" {
  vpc = true
  tags = {
    Name = "natgw"
  }
}

# NAT Gateway
resource "aws_nat_gateway" "nat_1a" {
  subnet_id     = aws_subnet.public[0].id
  allocation_id = aws_eip.nat_1a.id
  tags = {
    Name = lookup(var.vpc_name, "${terraform.workspace}.vpc_name")
  }
}
```

```route_table.tf
# Route table (private)
resource "aws_default_route_table" "private" {
  default_route_table_id = aws_vpc.main.default_route_table_id
  tags = {
    Name = "production private"
  }
}

# Route Table (public)
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "production public"
  }
}
```

```routing.tf
# Route (Private)
resource "aws_route" "private_1a" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id         = aws_vpc.main.default_route_table_id
  nat_gateway_id         = aws_nat_gateway.nat_1a.id
}

# Route (public)
resource "aws_route" "public" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id         = aws_route_table.public.id
  gateway_id             = aws_internet_gateway.main.id
}

# Association
resource "aws_route_table_association" "public" {
  count          = 6
  route_table_id = aws_route_table.public.id
  subnet_id      = element([aws_subnet.public[0].id, aws_subnet.public[1].id, aws_subnet.public[2].id, aws_subnet.public[3].id, aws_subnet.public[4].id, aws_subnet.public[5].id, ], count.index)
}
```

```subnet.tf
# public subnet

resource "aws_subnet" "public" {
  count                   = 6
  vpc_id                  = aws_vpc.main.id
  availability_zone       = element(lookup(var.az, "${terraform.workspace}.az"), count.index)
  cidr_block              = element(lookup(var.public_subnet_cidr, "${terraform.workspace}.public_cidr"), count.index)
  map_public_ip_on_launch = element(var.launch_ip, count.index)
  tags = {
    Name = element(var.public_tags, count.index)
  }
  lifecycle {
    ignore_changes = [tags]
  }
}

# private subnet

resource "aws_subnet" "private" {
  count             = 6
  vpc_id            = aws_vpc.main.id
  availability_zone = element(lookup(var.az, "${terraform.workspace}.az"), count.index)
  cidr_block        = element(lookup(var.private_subnet_cidr, "${terraform.workspace}.private_cidr"), count.index)
  tags = {
    Name = element(var.private_tags, count.index)
  }
  lifecycle {
    ignore_changes = [tags]
  }
}
```

```variables.tf
variable "region" {
  description = "AWS region."
  type        = map(string)
  default = {
    "default.region"        = "ap-northeast-1"
    "ap-northeast-1.region" = "ap-northeast-1"
    "us-west-2.region"      = "us-west-2"
    "us-east-1.region"      = "us-east-1"
  }
}

variable "vpc" {
  description = "AWS vpc."
  type        = map(string)
  default = {
    "default.vpc"        = "10.0.0.0/16"
    "ap-northeast-1.vpc" = "10.0.0.0/16"
    "us-west-2.vpc"      = "10.1.0.0/16"
    "us-east-1.vpc"      = "10.2.0.0/16"
  }
}

variable "vpc_name" {
  description = "vpc name."
  type        = map(string)
  default = {
    "default.vpc_name"        = "hogehoge"
    "ap-northeast-1.vpc_name" = "hogehoge"
    "us-west-2.vpc_name"      = "hogehoge"
    "us-east-1.vpc_name"      = "hogehoge"
  }
}

variable "az" {
  description = "availability zone."
  default = {
    "default.az"        = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d", "ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
    "ap-northeast-1.az" = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d", "ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
    "us-west-2.az"      = ["us-west-2a", "us-west-2c", "us-west-2d", "us-west-2a", "us-west-2c", "us-west-2d"]
    "us-east-1.az"      = ["us-east-1a", "us-east-1c", "us-east-1d", "us-east-1a", "us-east-1c", "us-east-1d"]
  }
}

variable "public_subnet_cidr" {
  description = "public subnet cidr block"
  default = {
    "default.public_cidr"        = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19", "10.0.192.0/22", "10.0.196.0/22", "10.0.200.0/22"]
    "ap-northeast-1.public_cidr" = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19", "10.0.192.0/22", "10.0.196.0/22", "10.0.200.0/22"]
    "us-west-2.public_cidr"      = ["10.1.0.0/19", "10.1.32.0/19", "10.1.64.0/19", "10.1.192.0/22", "10.1.196.0/22", "10.1.200.0/22"]
    "us-east-1.public_cidr"      = ["10.2.0.0/19", "10.2.32.0/19", "10.2.64.0/19", "10.2.192.0/22", "10.2.196.0/22", "10.2.200.0/22"]
  }
}

variable "private_subnet_cidr" {
  description = "private subnet cidr block"
  default = {
    "default.private_cidr"        = ["10.0.96.0/19", "10.0.128.0/19", "10.0.160.0/19", "10.0.204.0/22", "10.0.208.0/22", "10.0.212.0/22"]
    "ap-northeast-1.private_cidr" = ["10.0.96.0/19", "10.0.128.0/19", "10.0.160.0/19", "10.0.204.0/22", "10.0.208.0/22", "10.0.212.0/22"]
    "us-west-2.private_cidr"      = ["10.1.96.0/19", "10.1.128.0/19", "10.1.160.0/19", "10.1.204.0/22", "10.1.208.0/22", "10.1.212.0/22"]
    "us-east-1.private_cidr"      = ["10.2.96.0/19", "10.2.128.0/19", "10.2.160.0/19", "10.2.204.0/22", "10.2.208.0/22", "10.2.212.0/22"]
  }
}

variable "launch_ip" {
  default = ["true", "true", "true", "false", "false", "false"]
}

variable "public_tags" {
  default = ["public1", "public2", "public3", "elb public1", "elb public2", "elb public3"]
}

variable "private_tags" {
  default = ["private1", "private2", "private3", "elb private1", "elb private2", "elb private3"]
}
```

構成はざっとこんな感じです。
subnetはpublicとprivateをそれぞれ3つづつ作成、publicにはパブリックIPの自動割当をtrueにしています。CIDRの切り方はお好みでどうぞですね。
また、workspaceを切り替えることによって東京、バージニア、オレゴンにそれぞれvpcをapply出来るようになっています。
regionとsubnetを書き換えれば他のregionにもapply可能です。

ミソとしてはtagやIPその他設定を変数化し、variables.tfに変数を全て突っ込んでいるのでこのファイルだけ編集すれば他は弄らなくてもOKな書き方をしています（つもり）。
terraformは一回書いてしまえば色々使いまわしできる所も便利ですね。

ではまた