My Note

自己理解のためのブログ

TerraformでAWSの環境構築 ( VPC・NW )

やったこと

Terraformを利用してAWS環境を構築する。 今回は、VPCとNW周り(Subnet / RouteTable / NATGW / S3エンドポイント)をENVを3つの場合に分けて作成する。 Envはworkspaceを利用する。今回構築する環境は以下。

■ 構築する構成図 (workspace=prod)


f:id:yhidetoshi:20190618075255p:plain
terraform-vpc


■ Terraformのバージョンを 0.12.0版はこちらです。

実行環境

  • $ terraform --version
Terraform v0.11.11
+ provider.aws v1.60.0
・・・
# 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.tfAWSのクレデンシャルを設定した
    • modules 配下に resource を定義
    • ディレクトリ直下に module を定義
    • terraform.tfstate.d 配下にEnvごとのtfstateファイルが作成される
├── 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毎の場合分け

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を参考にした。

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でAWSVPCと関連するNW環境(Subnet/RouteTable/NAT-GW/S3エンドポイント)をAZ考慮して構築した。 workspaceを利用することで、環境を分離(DEV/STG/PROD)して構築することができた。