TerraformでAWSの環境構築 ( VPC・NW )
やったこと
Terraformを利用してAWS環境を構築する。
今回は、VPCとNW周り(Subnet / RouteTable / NATGW / S3エンドポイント)をENVを3つの場合に分けて作成する。
Envはworkspace
を利用する。今回構築する環境は以下。
■ 構築する構成図 (workspace=prod)
■ Terraformのバージョンを 0.12.0版はこちらです。
実行環境
$ terraform --version
Terraform v0.11.11 + provider.aws v1.60.0
MacOS(10.14.3)
.zshrc
・・・ # home/bin export PATH=$HOME/bin:$PATH ・・・
$ which terraform /Users/hidetoshi/bin/terraform
Env ( workspace ) について
workspaceで実行環境を分離することができる。Env毎に tfsate
ファイルが作成される
$ terraform workspace help
Usage: terraform workspace New, list, select and delete Terraform workspaces.
今回は以下のように3つのEnvを作成した
$ terraform workspace list
default dev prod * stg
- 実行したTerraformのディレクトリ構成
├── tfvars.tf ├── modules │ └── vpc │ └── main.tf ├── terraform.tfstate.d │ ├── dev │ ├── prod │ └── stg └── vpc.tf
NWの設計
- CIDR
- DEV: 10.12.0.0/16
- STG: 10.11.0.0/16
- PROD: 10.10.0.0/16
- AZ
- ap-northeast-1a: 10.X.Y.0/25
- ap-northeast-1c: 10.X.Y.128/25
- NAT-GW
- DEV: 1つ作成
- STG/PROD: AZに1つずつ作成
- SUBNET
- AZに分けて作成
- Public / Private をそれぞれAZ毎に複数
- S3エンドポイントはVPCにアタッチ
ENV毎の場合分け
"${terraform.workspace}"
を利用する- workspaceの値を参照できる
- https://www.terraform.io/docs/state/workspaces.html
locals
を利用する- ローカル変数として利用できる
- https://www.terraform.io/docs/configuration/locals.html
三項演算子を利用する
- Qiita: 三項演算子をもっと使おう
locals { cidr_block_prod = "10.10.0.0/16" cidr_block_stg = "10.11.0.0/16" cidr_block_dev = "10.12.0.0/16" public_subnets = "${split(",",(terraform.workspace == "prod" && terraform.workspace != "stg" && terraform.workspace != "dev") ? join(",", var.public_subnets_prod): join(",",var.public_subnets_dev))}" private_subnets = "${split(",",(terraform.workspace == "prod" && terraform.workspace != "stg" && terraform.workspace != "dev") ? join(",", var.private_subnets_prod): join(",",var.private_subnets_dev))}" }
上記の public_subnets
の書き方は以下のIssueを参考にした。
- conditional operator cannot be used with list values
module側
terraform/vpc.tf
variable "public_subnets_dev" { default = [ "10.12.0.0/25", "10.12.0.128/25", "10.12.1.0/25", "10.12.1.128/25", "10.12.2.0/25", "10.12.2.128/25", ] } variable "public_subnets_stg" { default = [ "10.11.0.0/25", "10.11.0.128/25", "10.11.1.0/25", "10.11.1.128/25", "10.11.2.0/25", "10.11.2.128/25", ] } variable "public_subnets_prod" { default = [ "10.10.0.0/25", "10.10.0.128/25", "10.10.1.0/25", "10.10.1.128/25", "10.10.2.0/25", "10.10.2.128/25", ] } variable "private_subnets_dev" { default = [ "10.12.3.0/25", "10.12.3.128/25", "10.12.4.0/25", "10.12.4.128/25", "10.12.5.0/25", "10.12.5.128/25", ] } variable "private_subnets_stg" { default = [ "10.11.3.0/25", "10.11.3.128/25", "10.11.4.0/25", "10.11.4.128/25", "10.11.5.0/25", "10.11.5.128/25", ] } variable "private_subnets_prod" { default = [ "10.10.3.0/25", "10.10.3.128/25", "10.10.4.0/25", "10.10.4.128/25", "10.10.5.0/25", "10.10.5.128/25", ] } locals { cidr_block_prod = "10.10.0.0/16" cidr_block_stg = "10.11.0.0/16" cidr_block_dev = "10.12.0.0/16" public_subnets = "${split(",",(terraform.workspace == "prod" && terraform.workspace != "stg" && terraform.workspace != "dev") ? join(",", var.public_subnets_prod): join(",",var.public_subnets_dev))}" private_subnets = "${split(",",(terraform.workspace == "prod" && terraform.workspace != "stg" && terraform.workspace != "dev") ? join(",", var.private_subnets_prod): join(",",var.private_subnets_dev))}" } module "vpc" { source = "./modules/vpc" name = "yhidetohsi-${terraform.workspace}" service_name = "com.amazonaws.ap-northeast-1.s3" exe_flag_vpcendpoint = "true" cidr_block = "${terraform.workspace == "prod" ? local.cidr_block_prod : terraform.workspace == "stg" ? local.cidr_block_stg :local.cidr_block_dev }" public_subnets = "${split(",",(terraform.workspace != "prod" && terraform.workspace == "stg" && terraform.workspace != "dev") ? join(",", var.public_subnets_stg): join(",",local.public_subnets))}" private_subnets = "${split(",",(terraform.workspace != "prod" && terraform.workspace == "stg" && terraform.workspace != "dev") ? join(",", var.private_subnets_stg): join(",",local.private_subnets))}" enable_nat_gateway = "true" nat_gateway_num = "${terraform.workspace == "dev" ? 1 : 2}" availability_zone = ["ap-northeast-1c", "ap-northeast-1d"] }
resource側
terraform/modules/vpc/main.tf
variable "name" {} variable "cidr_block" {} variable "public_subnets" { default = [] } variable "private_subnets" { default = [] } variable "availability_zone" { default = [] } variable "enable_dns_hostnames" { default = true } variable "enable_dns_support" { default = true } variable "enable_nat_gateway" { default = false } variable "private_propagating_vgws" { default = [] } variable "map_public_ip_on_launch" { default = false } variable "public_propagating_vgws" { default = [] } variable "tags" { default = {} } variable "main" { default = {} } variable "nat_gateway_num" { default = 0 } variable "service_name" {} variable "exe_flag_vpcendpoint" {} resource "aws_vpc" "mod" { cidr_block = "${var.cidr_block}" enable_dns_hostnames = "${var.enable_dns_hostnames}" enable_dns_support = "${var.enable_dns_support}" tags = "${merge(var.tags, map("Name", format("%s", var.name)))}" } resource "aws_internet_gateway" "mod_hoge" { vpc_id = "${aws_vpc.mod.id}" tags = "${merge(var.tags, map("Name", format("%s-igw", var.name)))}" } resource "aws_route_table" "public" { vpc_id = "${aws_vpc.mod.id}" propagating_vgws = ["${var.public_propagating_vgws}"] tags = "${merge(var.tags, map("Name", format("%s--rt-main", var.name)))}" } resource "aws_route_table" "private" { vpc_id = "${aws_vpc.mod.id}" propagating_vgws = ["${var.private_propagating_vgws}"] count = "${var.nat_gateway_num}" tags = "${merge(var.tags, map("Name", format("%s-nat-%s", var.name, element(var.availability_zone, count.index))))}" } resource "aws_route" "public_internet_gateway" { route_table_id = "${aws_route_table.public.id}" destination_cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.mod_hoge.id}" } resource "aws_route" "private_nat_gateway" { route_table_id = "${element(aws_route_table.private.*.id, count.index)}" destination_cidr_block = "0.0.0.0/0" nat_gateway_id = "${element(aws_nat_gateway.natgw.*.id, count.index)}" count = "${var.nat_gateway_num}" } resource "aws_subnet" "private" { vpc_id = "${aws_vpc.mod.id}" cidr_block = "${var.private_subnets[count.index]}" availability_zone = "${element(var.availability_zone, count.index)}" count = "${length(var.private_subnets)}" tags = "${merge(var.tags, map("Name", format("%s--subnet-private-%s", var.name, element(var.availability_zone, count.index))))}" } resource "aws_subnet" "public" { vpc_id = "${aws_vpc.mod.id}" cidr_block = "${var.public_subnets[count.index]}" availability_zone = "${element(var.availability_zone, count.index)}" count = "${length(var.public_subnets)}" tags = "${merge(var.tags, map("Name", format("%s--subnet-public-%s", var.name, element(var.availability_zone, count.index))))}" map_public_ip_on_launch = "${var.map_public_ip_on_launch}" } resource "aws_eip" "nateip" { vpc = true count = "${var.nat_gateway_num}" tags = "${merge(var.tags, map("Name", format("NATGW-%s-%s", var.name, element(var.availability_zone, count.index))))}" } resource "aws_vpc_endpoint" "private_s3" { count = "${var.exe_flag_vpcendpoint ? 1 : 0}" vpc_id = "${aws_vpc.mod.id}" service_name = "${var.service_name}" } resource "aws_vpc_endpoint_route_table_association" "private_subnet" { count = "${length(var.private_subnets) >= 1 && var.exe_flag_vpcendpoint ? length(var.private_subnets) : 0}" vpc_endpoint_id = "${aws_vpc_endpoint.private_s3.id}" route_table_id = "${element(aws_route_table.private.*.id, count.index)}" } resource "aws_vpc_endpoint_route_table_association" "public_subnet" { count = "${length(var.private_subnets) >= 1 && var.exe_flag_vpcendpoint ? length(var.private_subnets) : 0}" vpc_endpoint_id = "${aws_vpc_endpoint.private_s3.id}" route_table_id = "${element(aws_route_table.public.*.id, count.index)}" } resource "aws_nat_gateway" "natgw" { allocation_id = "${element(aws_eip.nateip.*.id, count.index)}" subnet_id = "${element(aws_subnet.public.*.id, count.index)}" count = "${var.nat_gateway_num}" tags = "${merge(var.tags, map("Name", format("NATGW-%s-%s", var.name, element(var.availability_zone, count.index))))}" depends_on = ["aws_internet_gateway.mod_hoge"] } resource "aws_route_table_association" "private" { count = "${length(var.private_subnets)}" subnet_id = "${element(aws_subnet.private.*.id, count.index)}" route_table_id = "${element(aws_route_table.private.*.id, count.index)}" } resource "aws_main_route_table_association" "main" { vpc_id = "${aws_vpc.mod.id}" route_table_id = "${aws_route_table.public.id}" }
まとめ
TerraformでAWSのVPCと関連するNW環境(Subnet/RouteTable/NAT-GW/S3エンドポイント)をAZ考慮して構築した。
workspace
を利用することで、環境を分離(DEV/STG/PROD)して構築することができた。