「世の中には難しいことが多い!」と感じることが多い私が、様々な用語を、初学者向けにわかりやすく全力で解説します。
解説する内容
Terraformのcountについて解説をします。複数のリソースを作成するための繰り返し処理としては、for_eachを使用する方が良いですが、まずは基礎としてcountについて理解すべきだと思い、解説記事を書きました。本記事では具体的なソースコードをもとに解説をしていきますので、ぜひ最後まで読んでみてください。
Terraformのcountとは
Terraformのcountは、少ないソースコードの量で複数のリソースを作成することができる便利な機能です。同じテンプレートを使用して、繰り返し処理をすることが出来ます。
以下のように、書くことで「N」に指定した数だけリソースを作成することが出来ます。
resource "resource_type" "resource_name" {
# リソースの設定値
# Nに指定した数だけリソースを作成する
count = N
}
例を用いて詳細に説明するよ
ソースコードをもとに具体的に説明をしていきます。
本記事の例としてterraformで作成するリソース
Google Cloudでサブネットを2つ作成します。「subnet-1」と「subnet-2」という名前の2つのサブネットを作成します。
前提条件として、プロジェクトやVPCネットワークが作成されている必要がありますが、今回はcountの使い方の説明のため、その部分は省略します。
サブネットが何かわからない方は以下の記事を読んでみてください。
countを使用した例
variable.tf
variable.tfでは、作成するサブネットの値を定義します。
# variable.tf
variable "subnet_list" {
description = "作成するサブネットのリスト"
# 値を格納するための変数を定義する
type = list(object({
name = string
region = string
cidr = string
}))
default = [
# 1つ目に作成するサブネットの値
{
name = "subnet-1"
region = "us-central1"
cidr = "10.0.1.0/24"
},
# 2つ目に作成するサブネットの値
{
name = "subnet-2"
region = "us-central1"
cidr = "10.0.2.0/24"
}
]
}
11行目からは作成するサブネットの値を記載しています。今回はnameで定義されているように、「subnet-1」と「subnet-2」いう名称の2つのサブネットを作成していきます。
countを指定するだけだとまったく同じ値のリソースを作成するだけになります。そのため、このようにvariable.tfにリソースの値を定義することで、別の値の2つのサブネットを作成します。
main.tf
main.tfでは、リソースを作成する処理を記載します。
# main.tf
resource "google_compute_subnetwork" "subnet" {
count = length(var.subnet_list) # 繰り返し処理のためサブネットの数を取得する
name = var.subnet_list[count.index].name
ip_cidr_range = var.subnet_list[count.index].cidr
region = var.subnet_list[count.index].region
network = google_compute_network.vpc_network.id # VPCを指定します(今回は無視でOK)
}
4行目では、countを使用してリソースをいくつ作成するのかを決めています。length(var.subnet_list)と記載することで、variable.tfのsubnet_listに記載された要素数を取得します。今回は2つなので、4行目は「count = 2」 となり、2つのサブネットを作成します。
5行目以降では、作成するサブネットの値を定義しています。count.indexは繰り返しの数を表します。1回目の処理ではcount.indexは0、2回目では1となります。そのため、1回目の処理でvar.subnet_list[count.index].nameはvar.subnet_list[0].nameとなり、variable.tfのsubnet_listに記載された1つ目の要素を取得します。
つまり、1回目の処理ではmain.tfは以下のようになります。
# main.tf
resource "google_compute_subnetwork" "subnet" {
count = 2
name = "subnet-1"
ip_cidr_range = "us-central1"
region = "10.0.1.0/24"
network = "test-vpc-network"
}
2回目の処理ではmain.tfは以下のようになります。
# main.tf
resource "google_compute_subnetwork" "subnet" {
count = 2
name = "subnet-2"
ip_cidr_range = "us-central2"
region = "10.0.2.0/24"
network = "test-vpc-network"
}
countを使用しない場合
上記の例で、countを使用しないでサブネットを2つ作成する場合は、main.tfは以下のように書きます。
# main.tf
resource "google_compute_subnetwork" "subnet1" {
name = var.subnet_list[0].name
ip_cidr_range = var.subnet_list[0].cidr
region = var.subnet_list[0].region
network = "test-vpc-network"
}
resource "google_compute_subnetwork" "subnet2" {
name = var.subnet_list[1].name
ip_cidr_range = var.subnet_list[1].cidr
region = var.subnet_list[1].region
network = "test-vpc-network"
}
作成するサブネットの数だけ、resourceブロックを記載しなければいけません。今回は2つだけですが、多くなってくるとソースコードの行数が増えてしまいます。
countを使用することでシンプルにコードを書くことが出来ることが分かります。
countはあまり使わないほうが良い?
実はcountはあまり使わないほうが良いといわれています。理由を先ほどのサブネットの例をもとに説明します。
先ほど作成した、subnet-1を削除したいとします。以下のようにコードを削除(コメントアウト)するだけでsubnet-1が削除されます。
# variable.tf
variable "subnet_list" {
description = "作成するサブネットのリスト"
# 値を格納するための変数を定義する
type = list(object({
name = string
region = string
cidr = string
}))
default = [
# subnet-1を削除するため、コメントアウトする
# {
# name = "subnet-1"
# region = "us-central1"
# cidr = "10.0.1.0/24"
# },
# 2つ目に作成するサブネットの値
{
name = "subnet-2"
region = "us-central1"
cidr = "10.0.2.0/24"
}
]
}
実は、先ほど作成したサブネットにはインデックスが割り当てられています。
削除前には以下のように割り当てられていたインデックスが、
【削除前】subnet-1:0、sunet-2:1
subnet-1を削除した後は以下のように変更されます。
【削除後】sunet-2:0
つまり、sunet-2のインデックスが詰められるため、sunet-2も一度削除されて、再作成されてしまいます。
再作成されてしまうと予期せぬエラーが発生する可能性があります。例えば、作成したリソースの情報を参照している場合にその値が変わってしまうかもしれません。本番環境であったら、再作成時にシステムが止まってしまいますね。
for_eachを使用することでこれを避けることが出来ます。for_eachでは別の記事で解説しますが、まずは基礎となるcountの考え方を理解してください。
こうやって使うと便利なcount
上記で説明したようなデメリットがあるため、あまり使用しないほうが良いように感じますが、countの便利な使い方を説明します。ループ処理ではなく、条件分岐に使用する方法です。
# variable.tf
variable "subnet_list" {
description = "作成するサブネットのリスト"
# 値を格納するための変数を定義する
type = list(object({
name = string
region = string
cidr = string
}))
default = [
# サブネットの値
{
name = "subnet-1"
region = "us-central1"
cidr = "10.0.1.0/24"
}
]
}
# main.tf
resource "google_compute_subnetwork" "subnet" {
count = var.subnet_list.name == "subnet-1" ? 1 : 0
name = var.subnet_list.name
region = var.subnet_list.region
ip_cidr_range = var.subnet_list.cidr
network = google_compute_network.vpc_network.id # VPCを指定します(今回は無視でOK)
}
4行目では、subnet_listのnameがsubnet-1という名称のサブネットが存在するときにサブネットを作成します。countが「0」のときはサブネットを作り、「1」の時にはサブネットを作らないというような条件分岐をすることが出来ます。
プログラミングに慣れている方は、if文を使えばよいのではないかと思いますが、Terraformにはif文はないのです!
まとめ
- countを使用することで、ループ処理によって複数のリソースを作成することが出来る
- しかし、ループ処理として使う場合はfor_eachを使った方が良い
- countは条件分岐としても使用することが出来る
参考
Udemyの以下の講座を参考にさせていただきました。複数の講座を視聴しましたが、個人的にはこちらの講座が一番分かりやすかったです。講座ではTerraformについて、初学者にも分かりやすく説明してくれているので、Terraformを学びたい方は視聴してみてください。
Infrastructure as Code のツールとしてよく使われる Terraform を、基本的な実装方法やコマンドから、モジュール化・複数環境の管理といった実践的な内容まで、AWS でのハンズオンでしっかり学びましょう!