CUEBiC TEC BLOG

キュービックTECチームの技術ネタを投稿しております。

Terraform Registryで公開されているTerraform Modulesが便利だった件

背景

こんにちは、キュービックでSREをやっているYuhta28です。キュービック内のテック技術について発信します。

過去何回かTerraformに関する記事を執筆しました。

cuebic.hatenablog.com

cuebic.hatenablog.com

この頃はTerraformを使い始めて日が浅く、作成リソースは全部手動で書かないといけないと考えていました。しかし、別のプロジェクトでAWS CDK1に触れる機会があり、AWS CDKのL2コンストラクトで構築してみたところ少ない記述量で必要な構成をすぐに用意してくれる便利さに魅了されました。
Terraformでも同じことがしたいと考え調べてみると、Terraform Registryで公開されているモジュールを使えば少ない記述量でリソースを作成できるみたいでしたのでモジュールを使ったTerraform IaCについて紹介いたします。

対象読者

  • TerraformでIaCしている人
  • 記述量が多くてもっと効率化したいと考えている人
  • Terraform Modulesの使い方を知りたい人

Terraform Modulesについて

registry.terraform.io

TerraformにはTerraform Registyという自由にダウンロードして使えるモジュールがいくつか存在します。例えばAWS関連のモジュールにはVPCを関連リソースを含めて作成してくれたり、EC2やRDSならセキュリティグループやIAMロールもセットに作成してくれるモジュールがあります。

https://registry.terraform.io/browse/modules?provider=aws

セットで書けるというのはどういうことかと言いますと一例を出します。
Amazon VPCを作成する場合、Terraformで書くと以下のようになります。

resource "aws_vpc" "main" {
  cidr_block  = "10.0.0.0/16"
  tags = {
    Name = "Cuebic-VPC"
  }
}

これをデプロイすればAWSCuebic-VPCというVPCが作成されます。しかしVPCにはサブネット、ルートテーブル、インターネットゲートウェイなど付随するリソースが数多くあり、Terraformはそれらすべてを別々のリソースとして記述しなければ作成されません。
パブリックサブネットとプライベートサブネットを含めた一般的なVPC関連リソースをTerraformでコード化すると、記述量が多くなり煩雑化してしまいます。

モジュールを使わないVPC作成

resource "aws_vpc" "terraform-vpc" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  tags = {
    Name = "${var.Tag_Name}-vpc"
  }
}

resource "aws_internet_gateway" "terraform-igw" {
  vpc_id = aws_vpc.terraform-vpc.id
  tags = {
    Name = "${var.Tag_Name}-igw"
  }
}
#--------------------------------------------------
# パブリックサブネットリソース
resource "aws_subnet" "terraform-public-subnet" {
  for_each                = var.public-AZ
  vpc_id                  = aws_vpc.terraform-vpc.id
  cidr_block              = each.value
  availability_zone       = "ap-northeast-1${each.key}"
  map_public_ip_on_launch = true
  tags = {
    Name = "terraform-${var.Tag_Name}-public-subnet-${each.key}"
  }
}
resource "aws_route_table" "terraform-public-rt" {
  for_each = var.public-AZ
  vpc_id   = aws_vpc.terraform-vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.terraform-igw.id
  }
  tags = {
    Name = "${var.Tag_Name}-public-rt-${each.key}"
  }
}
resource "aws_route_table_association" "terraform-public-rt-assoc" {
  for_each       = var.public-AZ
  subnet_id      = aws_subnet.terraform-public-subnet[each.key].id
  route_table_id = aws_route_table.terraform-public-rt[each.key].id
}

resource "aws_nat_gateway" "terraform-nat" {
  for_each  = toset(var.eip-NAT-AZ)
  subnet_id = aws_subnet.terraform-public-subnet[each.key].id
  depends_on = [
    aws_internet_gateway.terraform-igw
  ]
  allocation_id = aws_eip.terraform-nat-eip[each.key].id
  tags = {
    Name = "${var.Tag_Name}-nat-${each.key}"
  }
}
resource "aws_eip" "terraform-nat-eip" {
  for_each = toset(var.eip-NAT-AZ)
  tags = {
    Name = "${var.Tag_Name}-eip-${each.key}"
  }
  depends_on = [
    aws_internet_gateway.terraform-igw
  ]
}
#--------------------------------------------------
#--------------------------------------------------
# プライベートサブネットリソース
resource "aws_subnet" "terraform-private-subnet" {
  for_each          = var.private-AZ
  vpc_id            = aws_vpc.terraform-vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "terraform-${var.Tag_Name}-private-subnet-${each.key}"
  }
}
resource "aws_route_table" "terraform-private-rt" {
  for_each = var.private-AZ
  vpc_id   = aws_vpc.terraform-vpc.id
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = var.Tag_Name == "dev" ? aws_nat_gateway.terraform-nat["a"].id : aws_nat_gateway.terraform-nat[each.key].id
  }
  tags = {
    Name = "${var.Tag_Name}-private-rt-${each.key}"
  }
}
resource "aws_route_table_association" "terraform-private-rt-assoc" {
  for_each       = var.public-AZ
  subnet_id      = aws_subnet.terraform-private-subnet[each.key].id
  route_table_id = aws_route_table.terraform-private-rt[each.key].id
}

サブネットとルートテーブルは2つをアタッチするためのリソースとしてaws_route_table_associationも必要となり手作業で作成するとかなり苦労します。VPC関連リソースはサービス毎に大きく構成が変わるものではなく、基本的にパブリックサブネット、プライベートサブネット、データベースサブネットを各AZ毎内に作成されれば十分であるケースも多いです。

先程のTerraform RegistryにはVPC向けのモジュールもありましたのでモジュールを使ったリソース作成がどのようなものか見ていきましょう。

モジュールを使ったVPC作成

https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/lates

モジュールを使う場合、resourceブロックではなくmodulesブロックを使用します。sourceにはTerraform Registryで公開されているモジュールのパスを指定し、パラメーターに必要な値を入力しterraform applyすればVPC関連リソースが作成されます。

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
  public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
  database_subnets = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]

  enable_nat_gateway = true

  tags = {
    Terraform = "true"
    Environment = "dev"
  }
}

上記のコードを記述しますと3つのAZにパブリックサブネット、プライベートサブネット、データベースサブネット及びNATゲートウェイが作成されます。

VPCアーキテクチャ

パラメーターも多くはデフォルト値が設定されていますので最初から多く書く必要はなく必要なリソースを作成したいときに適宜パラメーターを変更すればOKです。

VPNゲートウェイを追加したい場合
enable_vpn_gateway = true

所感

Terraform Registryで公開されているモジュールを活用したTerraform運用について紹介しました。 最初の頃は頑張ってリソースをすべて書いていましたが、記述量が多くなりしんどいと感じていたところにモジュールを活用した方法を見つけられてTerraform開発が楽になりました。
Terraform Cloud2を使えば社内限定のプライベートモジュールも作成できますのでオリジナルモジュールを作成してぜひTerraform開発を効率化してみてください。