terraform AZにまたがる 複数 の サブネット を作成する方法

2022年2月16日AWS

今回はCloudformationではできない繰り返し処理を使って複数のサブネットを作ってみたまとめになります。

Cloudformationだと、ほぼ同じリソースを作成する場合でも、cidrを少し変えた定義を必要な数だけ書かないといけないんですよね…。

少数なら別に構わないのですが、数が多いとそれだけで数百行。。。

countの他にも外部ファイルからパラメータを渡すことで、動的かつ柔軟で再利用可能なテンプレートを作れます。

外部ファイルから値を取得する

TerraformやIaCに限らず何かを実行する際にパラメータとして変数に値を渡すことができるアレです。

Terraformでももちろんできます。必要なファイルは本体のmain.tf以外に2つ

variables.tf

main.tfで使う変数を定義するファイルです。

実行時にTerraformが自動で読み込んでくれます。書き方はざっくりこんな感じ

variable "tag_name" {
  description = "resource name prefix"
  type        = string
  default     = "tf"
}

variable "vpc_cidr" {
  description = "cidr block"
  type        = string
}

variable "subnet_cnt" {
  description = "subnet count"
  type        = number
}

簡単にいうと、実行時に渡される値を受け取る場所を定義したファイルですね。

variable "XXX"{

}

で囲っている部分で変数1つです.

実行時に値が渡されなかったらdefaultに指定されている値がmain.tfに渡されます。

terraform.tfvars

このファイルに渡したい値を書いていきます。書き方はこんな感じ

tag_name   = "hito-tf"
vpc_cidr   = "10.0.0.0/16"
subnet_cnt = 2

こうすることで毎回同じパラメータでterraformを実行することができます。

あと、これやることの何が良いかというと。

パスワードやアクセスキーなどの秘匿情報をコード内に書かなくても良いということです。

IaCはその性質上Githubのようなインターネット上のリポジトリで管理することが多いです。

つまり、インターネットに公開されるということで、そのファイルの中に重要なパスワードなどが平文のまま記載されているのはかなり危険です。

なので、そのような情報は別のファイルに保存してmain.tfを実行する時に読み込まれるようにすることで安全にIaCをすることができます。

だから、今回リンクを貼ったリポジトリにはこのファイルはありません。

私のPCの中だけにあります!この方法以外にも実行する際のコマンドでKeyとValueを指定する方法や、環境変数に値を仕込むほうほうもありますがこの方法が推奨だったような記憶です。

main.tf(参考)

パラメータを格納した変数をmain.tfで使うときはこんな感じです。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }
}


provider "aws" {
  profile = "default"
  region  = "ap-northeast-1"
}

resource "aws_vpc" "vpc" {
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"
  tags = {
    "Name" = "${var.tag_name}-vpc"
  }
}
var.[変数名]  #単体で使う場合はこう
${var.[変数名]}-vpc #タグ名などで文字列と連結する場合こう

複数のサブネットを作る

1つのリソースブロックで複数のリソースを作成する場合はcountを使います。

countで指定した回数だけそのブロックを繰り返し作成します。

また、countを指定した場合

count.index

という変数が使えるようになります。

これには最初0が格納されており繰り返しが行われる時に+1されていきます。

サブネットのタグにcount.indecを利用すると連番を振ることができます。

terraform.tfvarsにazのリストを定義します。

これはサブネットのazを分ける際に使います。

variable "tag_name" {
  description = "resource name prefix"
  type        = string
  default     = "tf"
}

variable "vpc_cidr" {
  description = "cidr block"
  type        = string
}

variable "subnet_cnt" {
  description = "subnet count"
  type        = number
}

### 以下追加
variable "az_list" {
  type    = list(string)
  default = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}

main.tfにもサブネットの記述を追加します。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.27"
    }
  }
}


provider "aws" {
  profile = "default"
  region  = "ap-northeast-1"
}

resource "aws_vpc" "vpc" {
  cidr_block       = var.vpc_cidr
  instance_tenancy = "default"
  tags = {
    "Name" = "${var.tag_name}-vpc"
  }
}

### 以下追記
resource "aws_subnet" "sbn" {
  count                   = var.subnet_cnt
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index)
  availability_zone       = var.az_list[count.index%3]
  map_public_ip_on_launch = true
  tags = {
    "Name" = "${var.tag_name}-sbn-${count.index}"
  }
}

サブネットでcidrをしているところでは

cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index)

という指定をしています。

これはサブネットをいい感じに分割してくれる関数です。

引数3つで左から「VPCのCIDR」、「ネットワーク部の増分」、「ネットワーク部の値」です。

3つ目を表す的確な言葉が見つからないのでいくつか例を。

cidrsubnet(10.0.0.0/16, 8, 1)
> 10.0.1.0/24
cidrsubnet(10.0.0.0/16, 8, 2)
> 10.0.2.0/24
cidrsubnet(10.0.0.0/16, 8, 3)
> 10.0.3.0/24

というようにCIDRを分割してくれる関数です。

これでterraform playを実行しますと。

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:

  # aws_subnet.sbn[0] will be created
  + resource "aws_subnet" "sbn" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "ap-northeast-1a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.0.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = true
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "hito-tf-sbn-0"
        }
      + tags_all                        = {
          + "Name" = "hito-tf-sbn-0"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_subnet.sbn[1] will be created
  + resource "aws_subnet" "sbn" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "ap-northeast-1c"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.0.1.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = true
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "hito-tf-sbn-1"
        }
      + tags_all                        = {
          + "Name" = "hito-tf-sbn-1"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_vpc.vpc will be created
  + resource "aws_vpc" "vpc" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.0.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "hito-tf-vpc"
        }
      + tags_all                         = {
          + "Name" = "hito-tf-vpc"
        }
    }

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

無事、2つのAZにサブネットを作成することができました。

結果を見るとわかりますが、繰り返しで作成したサブネットは0から始まる配列で作成されています。

なので、このサブネットにEC2を作成したりする場合はこの配列の番号を指定します。

AWS

Posted by kotaro