Terraform moduleの作り方

## はじめに
この記事はterraform初学者に向けてmoduleを自作する場合のやり方を載せていきます。
## moduleの作成
今回はVPC moduleを作成していきます。
moduleに関する情報は以下になります。

- 作成するもの
    - VPC
    - public subnet
    - private subnet
    - internet gateway
- input
    - project名
    - environment名
    - public subnetのcidrs
    - private subnetのcidrs
    - AZ
    - tags
- output
    - vpcのid
    - public subnetのid
    - private subnetのid

ファイル構成は以下のようになります

```
module/
  ├ examples/
    └ main.tf
    └ provider.tf
  ├ main.tf
  ├ outputs.tf
  └ variables.tf
```

provider.tfは各々の環境次第なので割愛して、実際にそれぞれのファイルを作成していきます。

### module本体
variables.tfには引数にとるものを書いていきます。
optionalな引数にはdefaultを定義します。

```variables.tf
variable "project" {
  description = "Project name"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
}

variable "public_subnet_cidrs" {
  description = "CIDR blocks for public subnets"
  type        = list(string)
}

variable "private_subnet_cidrs" {
  description = "CIDR blocks for private subnets"
  type        = list(string)
}

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
}

variable "tags" {
  description = "Tags to be applied to all resources"
  type        = map(string)
  default     = {}
}

```

main.tfでは実際に作成するリソースを記述していきます。

```main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(
    var.tags,
    {
      Name = "${var.project}-${var.environment}-vpc"
    }
  )
}

resource "aws_subnet" "public" {
  count             = length(var.public_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.public_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = merge(
    var.tags,
    {
      Name = "${var.project}-${var.environment}-public-subnet-${count.index + 1}"
      Type = "Public"
    }
  )
}

resource "aws_subnet" "private" {
  count             = length(var.private_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = merge(
    var.tags,
    {
      Name = "${var.project}-${var.environment}-private-subnet-${count.index + 1}"
      Type = "Private"
    }
  )
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = merge(
    var.tags,
    {
      Name = "${var.project}-${var.environment}-igw"
    }
  )
}

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

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

  tags = merge(
    var.tags,
    {
      Name = "${var.project}-${var.environment}-public-rt"
    }
  )
}

resource "aws_route_table_association" "public" {
  count          = length(var.public_subnet_cidrs)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}
```

outputsではmodule内で作成したリソースの中で、外部で使うものを書いていきます。

```outputs.tf
output "vpc_id" {
  description = "ID of the created VPC"
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "IDs of the created public subnets"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "IDs of the created private subnets"
  value       = aws_subnet.private[*].id
}
```

### 実際に使ってみる
では使ってみましょう。
examples配下のmain.tfに以下のような内容を記述します。

```main.tf
module "vpc" {
  source = "../"

  project     = "example"
  environment = "dev"
  vpc_cidr    = "10.0.0.0/16"

  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.10.0/24", "10.0.11.0/24"]
  availability_zones   = ["ap-northeast-1a", "ap-northeast-1c"]

  tags = {
    Environment = "staging"
    Terraform   = "true"
  }
}
```

```
> pwd
terraform-module/examples # example配下で以下のコマンドを実行します

> terraform init
> terraform plan
```

plan差分で9個のリソースが作成されることが期待値です
```
Plan: 9 to add, 0 to change, 0 to destroy.
```

実際に作成する場合には下記コマンドで実際のaws環境に反映させていきましょう。
```
> terraform apply
```

## まとめ
今回はterraform moduleを実例を含めて作成してみました。
基本的には以下三つのファイルを作成することでmoduleを作成することができます。これだけ覚えておけばとりあえずmoduleの形だけは作れるはずです。
あとはmain.tfで記述している場所を例えばvpc.tf、 subnet.tfと分けるなりしてよりみやすい構成を目指すのも良いと思います。

- main.tf
    - リソースを記述する
- variables.tf
    - 引数や変数を記述する
- outputs.tf
    - 作成したmoduleから使いたいものを記述する