Terraformでゾーン毎にsubnetを作成する(AWS, Alicloud)。

#高可用性なサーバー配置
可用性が高いサーバーの配置として、ゾーン毎に立てるのは常套手段と思う。クラウドベンダーの1つのゾーンに障害が発生しても、別のゾーンが生きていればサービスの継続性が保たれる。
その前準備として、ゾーン毎にsubnetを作成する手順をterraformで自動化する。
terraformへは、regionとVPCのCIDRのみを指定し後はよしなにやってくれること、AWSとAlicloudで同じような自動化ができることを確認する。

#input/output
**input**
- region
- VPCのCIDR

**output**
- VPC
- ゾーン毎のsubnet

つまり、入力のregionにVPCを作成して、そのregionが持つゾーン毎にsubnetを作成する。

#クラウド・プロバイダの設定
```tf:main_alicloud.tf
# Alicloud Providerの設定
provider "alicloud" {
  access_key = var.alicloud_access_key
  secret_key = var.alicloud_secret_key
  region     = var.alicloud_region
}
```
access_keyとsecret_keyはvariables.tfで設定するが、このような設定ファイルに残したくない場合は環境変数へ設定する方法もある。
それぞれ`ALICLOUD_ACCESS_KEY`と`ALICLOUD_SECRET_KEY`へ設定すれば良い。

```tf:main_aws.tf
# AWS Providerの設定
provider "aws" {
  region = var.aws_region
}
```
AWSでは、access_keyとsecret_keyは`~/.aws/credentials`に設定してある。

#利用できるゾーンを取得する

```tf:main_alicloud.tf
# 有効なゾーンを問い合わせ、local.all_zonesで参照する
data "alicloud_zones" "available" {
}
locals {
  all_zones = data.alicloud_zones.available.ids
}
```
alicloud_zonesでゾーンを問い合わせ、local変数のall_zonesへ設定している。
ちなみにAlicloudのap-northeast-1は、ゾーン数が2つであった。

- ap-northeast-1a
- ap-northeast-1b

```tf:main_aws.tf

# 有効なゾーンを問い合わせ、local.all_zonesで参照する
data "aws_availability_zones" "available" {
  state = "available"
}
locals {
  all_zones = data.aws_availability_zones.available.names
}
```
Alicloudとほとんど変わらないコード。
AWSのコードの方がより明確な指示ができる(`state = "available"`)。AlicloudはAsIsで同じように動作するだけかも。。

#VPCの作成

```tf:main_alicloud.tf
# VPCの作成
resource "alicloud_vpc" "vpc" {
  name       = "${var.base_name}-vpc"
  cidr_block = var.vpc_cidr
}
```
![vpc.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545831/fe85256d-8b77-67d1-dc38-b8cba05d0247.png)

```tf:main_aws.tf
# VPCの作成
resource "aws_vpc" "vpc" {
  cidr_block = var.vpc_cidr
  tags = map(
    "Name", "${var.base_name}-vpc",
  )
}
```
両クラウドでほとんど変わらないコード。VPCのCIDRはvariables.tfで設定する。

#subnetの作成

```tf:main_alicloud.tf
# vswitch(subnet)の作成
resource "alicloud_vswitch" "vsw" {
  count             = length(local.all_zones)
  name              = "${var.base_name}-${local.all_zones[count.index]}"
  vpc_id            = alicloud_vpc.vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, var.subnet_netmask_bits, var.subnet_offset + count.index)
  availability_zone = local.all_zones[count.index]
}
```
Alicloudではsubnetをvswitchと呼ぶらしい。
ゾーンの数分vswitchを作成している。ここでのミソは、vswitchのCIDRをcidrsubnet関数を使って生成しているところ。
VPCのCIDRが10.10.0.0/16の場合、bits=8,offset=0とすると、

- 10.10.0.0/24
- 10.10.1.0/24
- 10.10.2.0/24
- 　　：

とゾーンの数だけCIDRを生成してくれる。上で「よしなに」と書いたのはcidrsubnetの働きを指している。

```tf:main_aws.tf
# subnetの作成
resource "aws_subnet" "subnet" {
  count                   = length(local.all_zones)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = cidrsubnet(var.vpc_cidr, var.subnet_netmask_bits, var.subnet_offset + count.index)
  availability_zone       = local.all_zones[count.index]
  map_public_ip_on_launch = true
  tags = map(
    "Name", "${var.base_name}-${local.all_zones[count.index]}",
  )
}
```
Alicloudとほとんど変わらない。
![subnet.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545831/6e7bc290-7b07-8d52-8885-9d370616673b.png)

#main.tfのコード全体

```tf:main_alicloud.tf
# Alicloud Providerの設定
provider "alicloud" {
  access_key = var.alicloud_access_key
  secret_key = var.alicloud_secret_key
  region     = var.alicloud_region
}

# 有効なゾーンを問い合わせ、local.all_zonesで参照する
data "alicloud_zones" "available" {
}
locals {
  all_zones = data.alicloud_zones.available.ids
}

# VPCの作成
resource "alicloud_vpc" "vpc" {
  name       = "${var.base_name}-vpc"
  cidr_block = var.vpc_cidr
}

# vswitch(subnet)の作成
resource "alicloud_vswitch" "vsw" {
  count             = length(local.all_zones)
  name              = "${var.base_name}-${local.all_zones[count.index]}"
  vpc_id            = alicloud_vpc.vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, var.subnet_netmask_bits, var.subnet_offset + count.index)
  availability_zone = local.all_zones[count.index]
}
```

```tf:main_aws.tf
# AWS Providerの設定
provider "aws" {
  region = var.aws_region
}

# 有効なゾーンを問い合わせ、local.all_zonesで参照する
data "aws_availability_zones" "available" {
  state = "available"
}
locals {
  all_zones = data.aws_availability_zones.available.names
}

# VPCの作成
resource "aws_vpc" "vpc" {
  cidr_block = var.vpc_cidr
  tags = map(
    "Name", "${var.base_name}-vpc",
  )
}

# subnetの作成
resource "aws_subnet" "subnet" {
  count                   = length(local.all_zones)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = cidrsubnet(var.vpc_cidr, var.subnet_netmask_bits, var.subnet_offset + count.index)
  availability_zone       = local.all_zones[count.index]
  map_public_ip_on_launch = true
  tags = map(
    "Name", "${var.base_name}-${local.all_zones[count.index]}",
  )
}
```
#variables.tfのコード全体

```tf:variables_alicloud.tf
# 共通設定
variable "base_name" {
  default     = "test"
}

variable "vpc_cidr" {
  default = "10.10.0.0/16"
}

variable "subnet_offset" {
  default     = 0
}

variable "subnet_netmask_bits" {
  default     = 8
}

# クラウドベンダー依存部分
variable "alicloud_region" {
  default = "ap-northeast-1"
}

variable "alicloud_access_key" {
  default     = "XXXXXXXXXXXXXXXXXXXXXXXX"
}

variable "alicloud_secret_key" {
  default     = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
}
```
コードにkeyを書くと、こーゆー時にひと手間かかるから環境変数にしよう！

```tf:variables_aws.tf
# 共通設定
variable "base_name" {
  default     = "test"
}

variable "vpc_cidr" {
  default = "10.10.0.0/16"
}

variable "subnet_offset" {
  default     = 0
}

variable "subnet_netmask_bits" {
  default     = 8
}

# クラウドベンダー依存部分
variable "aws_region" {
  default = "ap-northeast-1"
}
```
#output - Alicloud

```tf
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

vpc = {
  "vpc" = {
    "cidr_block" = "10.10.0.0/16"
    "description" = ""
    "id" = "vpc-6wedpn6h4updttj8q26ej"
    "name" = "test-vpc"
    "resource_group_id" = "rg-acfns2xezp5njyq"
    "route_table_id" = "vtb-6wexwsiu3e97q1ed2canh"
    "router_id" = "vrt-6we4ws1qshqempzt2sd0s"
    "router_table_id" = "vtb-6wexwsiu3e97q1ed2canh"
  }
}
vsw = {
  "vsw" = [
    {
      "availability_zone" = "ap-northeast-1a"
      "cidr_block" = "10.10.0.0/24"
      "description" = ""
      "id" = "vsw-6weldnazsu76mf665lka6"
      "name" = "test-ap-northeast-1a"
      "vpc_id" = "vpc-6wedpn6h4updttj8q26ej"
    },
    {
      "availability_zone" = "ap-northeast-1b"
      "cidr_block" = "10.10.1.0/24"
      "description" = ""
      "id" = "vsw-6wewmrfku60rszr00wxzd"
      "name" = "test-ap-northeast-1b"
      "vpc_id" = "vpc-6wedpn6h4updttj8q26ej"
    },
  ]
}
```

#コード
https://github.com/settembre21/terraform-vpc-subnet

#さいごに
subnetまで作れたら後はインスタンス作ってLBで束ねるなり、なんなりできる。
regionとVPCのCIDRを指定するだけ。開発環境・テスト環境・・◯◯環境がすぐに構築できる。

terraformは、その記述自体が仕様書のようなものである。AWS向けに書いたコードをAlicloudチームへ渡すことで余計な説明が不要になるのではないか、と思う。これもインフラのコード化による産物だろう。