TerraformでAWS VPCを作成する

Terraformで以下のような構成のVPCを作成します。
VPCのデフォルトのルートテーブル、ネットワークACL、DHCP オプションセット、セキュリティーグループを利用するようにしています。

![qiita.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/452ef941-afbd-6d5e-9209-45039bc834d4.png)

# ディレクトリ構成
```
-| environment
 | |- dev
 |    |- main.tf
 |    |- provider.tf
 |- modules
    |- main.tf
    |- data.tf
    |- variables.tf

```

# ソース
<details><summary>environment/dev/provider.tf</summary><div>

```hcl:environment/dev/provider.tf
terraform {
  required_version = "~> 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.8.0"
    }
  }
  cloud {
    organization = "urushibata-org"
    workspaces {
      name = "aws-develop-terraform"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"

  default_tags {
    tags = {
      ManagedBy = "terraform"
    }
  }
}
```
</div></details>

```hcl:environment/dev/main.tf
locals {
  environment = "dev"
}

module "vpc" {
  source      = "../../modules/vpc"
  environment = local.environment
  vpc_cidr    = "192.168.0.0/24"
}
```

```hcl:modules/vpc/data.tf
# 現在のリージョンを取得します。
data "aws_region" "current" {}
```
```hcl:modules/vpc/varibles.tf
variable "environment" {
  type = string
}

variable "vpc_cidr" {
  type = string
}
```
VPCを作成するとAWSが強制的にデフォルトのルートテーブル、ネットワークACL、DHCP オプションセット、セキュリティーグループを作成します。
デフォルトのルートテーブル、ネットワークACL、セキュリティーグループは1つのVPCと1:1です。つまり、VPCを作成する毎に新しく作成されます。ですが、DHCP オプションセットについてはリージョンで1つです。既にデフォルトDHCP オプションセットが存在していたら作成されません。ここではそれらデフォルトのリソースを利用するように記述しています。
デフォルトのリソース`aws_default_network_acl`、`aws_default_security_group`、`aws_default_route_table`、`aws_default_vpc_dhcp_options`は特殊でAWSが自動作成したものの設定を上書きするという動きをします。正直すこし使いにくいので、ハマりたくない人は`aws_network_acl`といった通常のリソースを使用しましょう。

```hcl:modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"

  tags = {
    Name = "main"
  }
}

resource "aws_subnet" "subnets" {
  count = 2

  vpc_id     = aws_vpc.main.id
  # cidrsubnet関数を使ってCIDRを分割します。第2引数はビット数なので注意。
  cidr_block = cidrsubnet(var.vpc_cidr, 1, count.index)

  tags = {
    Name = "subnet-${count.index}"
  }
}

resource "aws_internet_gateway" "igw" {
  # ここでVPCを指定しておくと aws_internet_gateway_attachment でアタッチする必要がなくなります。
  # どちらで書いても大丈夫です。
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "igw"
  }
}

resource "aws_default_network_acl" "default_acl" {
  # 自動で作成されたネットワークACLのIDを渡します。
  default_network_acl_id = aws_vpc.main.default_network_acl_id

  ingress {
    protocol   = -1
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  egress {
    protocol   = -1
    rule_no    = 100
    action     = "allow"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  tags = {
    Name = "default-acl"
  }
}

resource "aws_default_security_group" "default_sg" {
  # ここはVPCのID。うーん、わかりにくい。
  vpc_id = aws_vpc.main.id

  ingress {
    protocol  = -1
    self      = true
    from_port = 0
    to_port   = 0
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "default-sg"
  }
}

resource "aws_default_route_table" "public_rt" {
  # 自動で作成されたルートテーブルのIDを渡します。
  default_route_table_id = aws_vpc.main.default_route_table_id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "default-rt"
  }
}

resource "aws_route_table" "private_rt" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "private-rt"
  }
}

resource "aws_route_table_association" "rt_asso" {
  subnet_id      = aws_subnet.subnets[1].id
  route_table_id = aws_route_table.private_rt.id
}

resource "aws_default_vpc_dhcp_options" "default_dhcp_op" {
  # リージョンで1つなのでIDを渡さなくても判定できる。
  tags = {
    Name = "default-dhcp-op"
  }
}

resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.${data.aws_region.current.name}.s3"

  tags = {
    Name = "s3-vpc-endpoint"
  }
}

resource "aws_vpc_endpoint_route_table_association" "example" {
  route_table_id  = aws_route_table.private_rt.id
  vpc_endpoint_id = aws_vpc_endpoint.s3.id
}
```

# 実行結果
想定通りのVPCが作成されました。
![vpc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/f448531b-97c2-a35c-961c-d85eb5a13adf.png)