Terraform を用いて VPC を定義する サンプルコード を作成しました。
バグや改善点などがあれば、教えてください。コメントでも PR でも大歓迎です。
工夫
- NAT ゲートウェイを作成するかどうかを変数によって選ぶことができる
- パブリックサブネットを作る AZ (アベイラビリティゾーン) と プライベートサブネットを作る AZ を異なるように設定できる
- サブネットに割り当てる Cidr block の大きさを設定できる(現状は、すべてのサブネットで一定値でしか設定できません)
それぞれ、何が嬉しいかを説明します。
NAT ゲートウェイを作成するかどうかを変数によって選ぶことができる
NAT ゲートウェイは有料のリソースです。必要ない場合にはたてたくありません。コストを抑えることにつながります。
また、外部との接続のために VPC エンドポイントを使用する際も、OFF の設定ができると便利です。aws_route
リソースを作成することでルートテーブルに新しくルートを追加することができます。
パブリックサブネットを作る AZ と プライベートサブネットを作る AZ を異なるように設定できる
検証用の環境などで、可用性が必要ないケースもあると思います。そのような際に、不必要なサブネットの作成を抑制できます。
サブネットに割り当てる cidr block の大きさを設定できる
プライベートサブネットにより大きな値が必要になるケースも多々あると思いました。別々の値が指定できるようにするべきでしたが、大変そうでやめました。
cidr の計算が大変で、 cidrsubnet
関数で対応できる範囲の工夫になりました。
Terraform コードに関する解説
Terraform コードを書いていて、ポイントだと思った点を解説します。
cidr の分割
subnet_cidr_blocks = {
public = {
for i, v in var.public_availability_zones :
v => cidrsubnet(var.vpc_cidr_block, var.subnet_cidr_block_newbit, i)
}
private = {
for i, v in var.private_availability_zones :
v => cidrsubnet(var.vpc_cidr_block, var.subnet_cidr_block_newbit, i + length(var.public_availability_zones))
}
}
上のように cidrsubnet
関数と for文を用いて分解しています。これによって、AZ 名をキーとして cidr block を値とする連想配列が作成できます。
NAT ゲートウェイ (Elastic IP) を作成するかどうかの選択
resource "aws_eip" "this" {
count = var.need_nat_gateway ? 1 : 0
domain = "vpc"
tags = merge(local.tags, {
Name = "${var.prefix}-eip-for-nat-gateway"
})
}
resource "aws_nat_gateway" "this" {
count = var.need_nat_gateway ? 1 : 0
allocation_id = aws_eip.this[0].id
# TODO: Since the order of Availability Zones is not guaranteed,
# the chosen subnet from the list may vary across runs or regions.
subnet_id = values(aws_subnet.public)[0].id
tags = merge(local.tags, {
Name = "${var.prefix}-nat-gateway"
})
depends_on = [aws_internet_gateway.this]
}
上のように、 count = var.need_nat_gateway ? 1 : 0
と三項演算子を使用することで、作成する数を調整しています。
注意点として、 aws_eip.this, aws_nat_gateway.this
を参照する際は aws_eip.this[0], aws_nat_gateway.this[0]
としなければならなくなります。なぜなら、 count
によって配列としてリソースが定義されているからです。
また、 var.need_nat_gateway = false
の際に、 aws_eip.this[0], aws_nat_gateway.this[0]
による参照が、エラーになります。空の配列となるからです。
細かな点ですが、 aws_nat_gateway.this
の subnet_id
の指定も values(aws_subnet.public)[0].id
となっています。aws_subnet.public
は for_each
によって作成されているので AZ 名をキーとする連想配列によってリソースが定義されています。これをインデックスで参照したいので、値の配列を values
関数によって作成しています。今回は、どこに NAT ゲートウェイを作成するかを選べない仕様になっています。
プライベートサブネットのルートテーブル
resource "aws_route_table" "private" {
vpc_id = aws_vpc.this.id
dynamic "route" {
for_each = var.need_nat_gateway ? [aws_nat_gateway.this[0].id] : []
content {
cidr_block = "0.0.0.0/0"
nat_gateway_id = route.value
}
}
tags = merge(local.tags, {
Name = "${var.prefix}-private-route-table"
})
depends_on = [aws_nat_gateway.this]
}
dynamic
構文が使用されています。NAT ゲートウェイを作成するかどうかを変数によって選択できるようにしました。 NAT ゲートウェイを作成する場合は、ルートとして追加したいです。一方で作成しない場合は、ルートを空にしたいです。
このように、変数によってリソースの設定を動的に切り替える必要がある時に、 dynamic
構文がとても役に立ちます。
使い方
module "vpc" {
source = ...
}
のようにモジュールとしても使用できます。main.tf
とバージョン指定がしてあります。これ以外のバージョンでも大体動くと思いますが、動かないかもしれません。
変数指定の例は、 terraform.tfvars.tmpl
にしてあります。これを terraform.tfvars
とリネームして使用すると簡単かもしれません。