MORE

BLOG

IaCツールでインフラ構築してみた②

  • IaC
  • Shopify
  • Tips
  • インフラ
  • ツール
  • 開発
25.07.03

はじめに

1.1 記事について

テックディレクションではプロジェクトマネジメント業務に加えて、SaaS型ECプラットフォームShopifyを中心にECストアの構築および標準機能では対応できない要件を満たすアプリケーション開発も行っております。

業務で得たアプリケーションの開発方法のノウハウを複数記事に分けて投稿しようと思います!「IaCでインフラ構築やってみた」シリーズではアプリケーションのインフラをIaC(Terraform、SAM、Serverless Frameworkなど)での構築手順を解説します。

本記事は「IaCでインフラ構築やってみた」シリーズ第二弾として、Terraformを用いてVPCにインターネットゲートウェイとサブネットを構築する方法について記載しています。

1.2 記事の対象

・アプリケーションエンジニアだがインフラに興味があるまたは構築をしなければならない方

・ざっくりTerraformでのインフラ構築手順を知りたい方

この記事で触れないこと

・AWS各サービスの説明

・Terraform(利用する場合はtenvも)の文法

1.3 記事

この記事を読み終えるまでに、約20分です。

前提条件

IaCツールでインフラ構築してみた①」を読んでいること

ディレクトリ構成

test-infra
 ├─ igw   # 追加
 │   ├─ variables.tf   # 追加
 │   ├─ outputs.tf   # 追加
 │   └─ main.tf   # 追加
 ├─ route_table   # 追加
 │   ├─ variables.tf   # 追加
 │   ├─ outputs.tf   # 追加
 │   └─ main.tf   # 追加
 ├─ subnet # 追加
 │   ├─ variables.tf   # 追加
 │   ├─ outputs.tf   # 追加
 │   └─ main.tf   # 追加
 ├─ vpc
 │   ├─ variables.tf
 │   ├─ outputs.tf   # 追加
 │   └─ main.tf
 ├─ main.tf   # 変更
 ├─ terraform.tfvars   # 変更
 └─ variables.tf   # 変更

ソース詳細

共通

variables.tf

リソース全体の変数定義

# 前回記述箇所の後ろに追加
variable "public_cidr" {}
variable "private_cidr" {}

terraform.tfvars

リソース全体の変数値定義

# 前回記述箇所の後ろに追加
public_cidr  = "10.201.0.0/24"
private_cidr = "10.201.1.0/24"

main.tf

# 前回記述箇所の後ろに追加
module "igw" {
  source = "./igw"

  app_name = var.app_name
  vpc_id   = module.vpc.vpc_id
}

module "subnet" {
  source = "./subnet"

  app_name     = var.app_name
  vpc_id       = module.vpc.vpc_id
  public_cidr  = var.public_cidr
  private_cidr = var.private_cidr
}

module "route_table" {
  source = "./route_table"

  app_name          = var.app_name
  vpc_id            = module.vpc.vpc_id
  igw_id            = module.igw.igw.id
  public_subnet_id  = module.subnet.public_id
  private_subnet_id = module.subnet.private_id
}

vpcディレクトリ

outputs.tf

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

igwディレクトリ

variables.tf

variable "app_name" {
  type = string
}

variable "vpc_id" {
  type = string
}

outputs.tf

output "igw" {
  description = "Internet Gateway Object"
  value       = aws_internet_gateway.main
}

main.tf

resource "aws_internet_gateway" "main" {
  vpc_id = var.vpc_id

  tags = {
    Name = "${var.app_name}_igw"
  }
}

subnetディレクトリ

variables.tf

variable "app_name" {
  type = string
}

variable "vpc_id" {
  type = string
}

variable "public_cidr" {
  type = string
}

variable "private_cidr" {
  type = string
}

outputs.t

# Public Subnet
resource "aws_subnet" "public" {
  vpc_id     = var.vpc_id
  cidr_block = var.public_cidr

  tags = {
    Name = "${var.app_name}_public"
  }
}

# Private Subnet
resource "aws_subnet" "private" {
  vpc_id     = var.vpc_id
  cidr_block = var.private_cidr

  tags = {
    Name = "${var.app_name}_private"
  }
}

main.tf

# Public Subnet
resource "aws_subnet" "public" {
  vpc_id     = var.vpc_id
  cidr_block = var.public_cidr

  tags = {
    Name = "${var.app_name}_public"
  }
}

# Private Subnet
resource "aws_subnet" "private" {
  vpc_id     = var.vpc_id
  cidr_block = var.private_cidr

  tags = {
    Name = "${var.app_name}_private"
  }
}

route_tableディレクトリ

variables.tf

variable "app_name" {
  type = string
}

variable "vpc_id" {
  type = string
}

variable "igw_id" {
  type = string
}

variable "public_subnet_id" {
  type = string
}

variable "private_subnet_id" {
  type = string
}

outputs.tf

output "public_id" {
  description = "Route Table ID for Public Subnet"
  value       = aws_route_table.public.id
}

output "private_id" {
  description = "Route Table ID for Private Subnet"
  value       = aws_route_table.private.id
}

main.tf

# Public Subnet Route Table
resource "aws_route_table" "public" {
  vpc_id = var.vpc_id

  tags = {
    Name = "${var.app_name}_rt_public"
  }
}

resource "aws_route" "igw" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = var.igw_id
}

# associate with Public Subnet Route Table
resource "aws_route_table_association" "public" {
  route_table_id = aws_route_table.public.id
  subnet_id      = var.public_subnet_id
}

# Private Subnet Route Table
resource "aws_route_table" "private" {
  vpc_id = var.vpc_id

  tags = {
    Name = "${var.app_name}_rt_private"
  }
}

# associate with Private Subnet Route Table
resource "aws_route_table_association" "private" {
  route_table_id = aws_route_table.private.id
  subnet_id      = var.private_subnet_id
}

構築

1. モジュールを読み込む

$ terraform init
Initializing the backend...
Initializing modules...
- igw in igw
- route_table in route_table
- subnet in subnet
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v4.67.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

今回モジュールとして、igwディレクトリ、subnetディレクトリ、route_tableディレクトリを作成したのでterraformがモジュールディレクトリを認識させます

$ terraform plan 
╷
│ Error: Module not installed
│ 
│   on main.tf line 31:
│   31: module "igw" {
│ 
│ This module is not yet installed. Run "terraform init" to install all modules required by this configuration.
╵
╷
│ Error: Module not installed
│ 
│   on main.tf line 38:
│   38: module "subnet" {
│ 
│ This module is not yet installed. Run "terraform init" to install all modules required by this configuration.
╵
╷
│ Error: Module not installed
│ 
│   on main.tf line 47:
│   47: module "route_table" {
│ 
│ This module is not yet installed. Run "terraform init" to install all modules required by this configuration.

initコマンドを実行しない状態でplanコマンドを実行すると、モジュールディレクトリが認識されずエラーメッセージが表示されます(筆者も久しぶりに触ったら怒られてしまいました…)

2. 構築予定のリソースを確認する

$ terraform plan
module.vpc.aws_vpc.main: Refreshing state... [id=vpc-0625e06c95053e41f]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # module.igw.aws_internet_gateway.main will be created
  + resource "aws_internet_gateway" "main" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "test_igw"
        }
      + tags_all = {
          + "Name" = "test_igw"
        }
      + vpc_id   = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route.igw will be created
  + resource "aws_route" "igw" {
      + destination_cidr_block = "0.0.0.0/0"
      + gateway_id             = (known after apply)
      + id                     = (known after apply)
      + instance_id            = (known after apply)
      + instance_owner_id      = (known after apply)
      + network_interface_id   = (known after apply)
      + origin                 = (known after apply)
      + route_table_id         = (known after apply)
      + state                  = (known after apply)
    }

  # module.route_table.aws_route_table.private will be created
  + resource "aws_route_table" "private" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = (known after apply)
      + tags             = {
          + "Name" = "test_rt_private"
        }
      + tags_all         = {
          + "Name" = "test_rt_private"
        }
      + vpc_id           = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route_table.public will be created
  + resource "aws_route_table" "public" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = (known after apply)
      + tags             = {
          + "Name" = "test_rt_public"
        }
      + tags_all         = {
          + "Name" = "test_rt_public"
        }
      + vpc_id           = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route_table_association.private will be created
  + resource "aws_route_table_association" "private" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # module.route_table.aws_route_table_association.public will be created
  + resource "aws_route_table_association" "public" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # module.subnet.aws_subnet.private will be created
  + resource "aws_subnet" "private" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.201.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "test_private"
        }
      + tags_all                                       = {
          + "Name" = "test_private"
        }
      + vpc_id                                         = "vpc-0625e06c95053e41f"
    }

  # module.subnet.aws_subnet.public will be created
  + resource "aws_subnet" "public" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.201.0.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "test_public"
        }
      + tags_all                                       = {
          + "Name" = "test_public"
        }
      + vpc_id                                         = "vpc-0625e06c95053e41f"
    }

Plan: 8 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
you run "terraform apply" now.

3. リソースを構築する

$ terraform apply
module.vpc.aws_vpc.main: Refreshing state... [id=vpc-0625e06c95053e41f]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # module.igw.aws_internet_gateway.main will be created
  + resource "aws_internet_gateway" "main" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "test_igw"
        }
      + tags_all = {
          + "Name" = "test_igw"
        }
      + vpc_id   = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route.igw will be created
  + resource "aws_route" "igw" {
      + destination_cidr_block = "0.0.0.0/0"
      + gateway_id             = (known after apply)
      + id                     = (known after apply)
      + instance_id            = (known after apply)
      + instance_owner_id      = (known after apply)
      + network_interface_id   = (known after apply)
      + origin                 = (known after apply)
      + route_table_id         = (known after apply)
      + state                  = (known after apply)
    }

  # module.route_table.aws_route_table.private will be created
  + resource "aws_route_table" "private" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = (known after apply)
      + tags             = {
          + "Name" = "test_rt_private"
        }
      + tags_all         = {
          + "Name" = "test_rt_private"
        }
      + vpc_id           = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route_table.public will be created
  + resource "aws_route_table" "public" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = (known after apply)
      + tags             = {
          + "Name" = "test_rt_public"
        }
      + tags_all         = {
          + "Name" = "test_rt_public"
        }
      + vpc_id           = "vpc-0625e06c95053e41f"
    }

  # module.route_table.aws_route_table_association.private will be created
  + resource "aws_route_table_association" "private" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # module.route_table.aws_route_table_association.public will be created
  + resource "aws_route_table_association" "public" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # module.subnet.aws_subnet.private will be created
  + resource "aws_subnet" "private" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.201.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "test_private"
        }
      + tags_all                                       = {
          + "Name" = "test_private"
        }
      + vpc_id                                         = "vpc-0625e06c95053e41f"
    }

  # module.subnet.aws_subnet.public will be created
  + resource "aws_subnet" "public" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.201.0.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "test_public"
        }
      + tags_all                                       = {
          + "Name" = "test_public"
        }
      + vpc_id                                         = "vpc-0625e06c95053e41f"
    }

Plan: 8 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 

で入力待ち状態となるので「yes」と入力します。

Enter a value: yes

module.igw.aws_internet_gateway.main: Creating...
module.route_table.aws_route_table.public: Creating...
module.route_table.aws_route_table.private: Creating...
module.subnet.aws_subnet.private: Creating...
module.subnet.aws_subnet.public: Creating...
module.route_table.aws_route_table.public: Creation complete after 1s [id=rtb-01593e4b6a8a9df2b]
module.route_table.aws_route_table.private: Creation complete after 1s [id=rtb-093c4502e8889b365]
module.igw.aws_internet_gateway.main: Creation complete after 1s [id=igw-0459d26c578cd181e]
module.route_table.aws_route.igw: Creating...
module.subnet.aws_subnet.private: Creation complete after 1s [id=subnet-09ab3d668829dab34]
module.route_table.aws_route_table_association.private: Creating...
module.subnet.aws_subnet.public: Creation complete after 1s [id=subnet-0fe7166f20c853f6a]
module.route_table.aws_route_table_association.public: Creating...
module.route_table.aws_route_table_association.private: Creation complete after 0s [id=rtbassoc-042b85e46da36bdf3]
module.route_table.aws_route_table_association.public: Creation complete after 0s [id=rtbassoc-09c3ec6bd50fb098b]
module.route_table.aws_route.igw: Creation complete after 0s [id=r-rtb-01593e4b6a8a9df2b1080289494]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

planで確認した内容の通り各リソースが作成されます。

4. 構築したリソースを確認する

インターネットゲートウェイ

IaCツールでインフラ構築してみた② - 株式会社テックディレクション

→インターネットゲートウェイが作成され、前回作成したVPCに紐付いています

サブネット

IaCツールでインフラ構築してみた② - 株式会社テックディレクション

→パブリックサブネットとプライベートサブネットが作成され、それぞれ前回作成したVPCに紐付いています

ルートテーブル

IaCツールでインフラ構築してみた② - 株式会社テックディレクション

→ルートテーブルが作成され、今回作成したサブネットに紐付いています

IaCツールでインフラ構築してみた② - 株式会社テックディレクション

→パブリックサブネットに紐付いています。インターネットゲートウェイへのルートも設定されています。

IaCツールでインフラ構築してみた② - 株式会社テックディレクション

→プライベートサブネットに紐付いています。

まとめ

Terraformで簡単にサブネットを構築できました。

次回からインターネット接続するためのリソースを作成します!

著者:なべちゃん

弊社では、ECサイトのリプレース案件から、Shopifyカスタムアプリ開発、保守案件に至るまで、EC中心にプロジェクトの質にこだわり、お客様に笑顔になってもらえるよう日々邁進しております。

皆様からのお問い合わせ・ご相談をお待ちしております。

コメントはこちら