0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform講座:繰り返し処理編

Posted at

今回の記事について

仕事内容が今年度から変わったため、お久しぶりの投稿になってしまいました。。。
今回は、Terraform講座の第5回目として、繰り返し処理について解説しようと思います!

繰り返し処理とは?

東京リージョン内のVPCに、AZごとにプライベートサブネットを作りたいとき、皆さんならどうTerraformコードを書きますか?
パッと思いつくのは、1つ1つリソースを書く方法だと思います。

resource "aws_subnet" "private-1a" {
    vpc_id            = aws_vpc.main.id
    cidr_block        = "10.0.0.0/24"
    availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "private-1c" {
    vpc_id            = aws_vpc.main.id
    cidr_block        = "10.0.1.0/24"
    availability_zone = "ap-northeast-1c"
}

resource "aws_subnet" "private-1d" {
    vpc_id            = aws_vpc.main.id
    cidr_block        = "10.0.2.0/24"
    availability_zone = "ap-northeast-1d"
}

直感的で手を動かせばいいだけですが、かなり似たようなコードになっています。
さらに「パブリックサブネットとプライベートサブネット1つずつ作りましょう!」となると、合計で6個も似たようなコードを書くことになり、オプションが増えたりすると管理がどんどん煩雑になります。以上のコーディングの労力と管理を楽にするために、「1つのテンプレのようなコードを書いて欲しい数だけリソースを作成させる」 というのが繰り返し処理です。

Terraformでは、countfor_eachで繰り返し処理を実現することが出来ます!

countによる繰り返し処理

countを使用した例文

countによる繰り返し処理は、作成したい数を指定して繰り返し処理を行います。
作成したい数については基本的にあらかじめ配列を定義したうえで、その長さを指定することが多いです。
例えば、先ほどのサブネット作成を例にするとAZ名の配列とCIDRの配列を用意しておき、それらの長さをもとに繰り返し処理を定義します。

# AZ名の配列
variable "az_names" {
    default = [
        "ap-northeast-1a",
        "ap-northeast-1c",
        "ap-northeast-1d"
    ]
}

# CIDRの配列
variable "cidr_block" {
    default = [
        "10.0.0.0/24",
        "10.0.1.0/24",
        "10.0.2.0/24"
    ]
}

resource "aws_subnet" "private" {
    # まず、countを記述
    count             = length(var.az_names)
    vpc_id            = aws_vpc.main.id
    cidr_block        = cidr_block[count.index]
    availability_zone = az_names[count.index]
}

これにより、1つの aws_subnet リソースのコードを記述するだけで配列の長さ=3つ分のサブネットを作成することが出来ます!とてもスマートになりました!!

countの難点

さて、このcountですが、コーディング負担は楽になる一方、Terraform内のリソース管理で少し難しくなってしまうケースがあります。
というのも、実際にPlan結果を見てみると、作成されるリソースは

# aws_subnet.private["0"] will be created
# aws_subnet.private["1"] will be created
# aws_subnet.private["2"] will be created

となります。これでは、先ほどのコードと比較して、どのサブネットがどのAZに構築したリソースなのか直感的に分かりづらくなっています。
では、もっといい方法が無いかというので、今度はmapを使用して繰り返し処理を行うfor_eachについて紹介します。

for_eachによる繰り返し処理

mapとは?

いわゆる辞書型のデータで、以下のようにKey-Value形式で値が対応しています。

variable "cidr_block" {
    type = map(string)
    default = {
        "ap-notrheast-1a" = "10.0.0.0/24"
        "ap-northeast-1c" = "10.0.1.0/24"
        "ap-northeast-1d" = "10.0.2.0/24"
    }
}

例えば、cidr_block["ap-northeast-1a"] = "10.0.0.0/24"となります。
そして、for_each文では、Keyによるリソース名を作成することが出来ます。

for_eachを使用した例文

for_eachによるサブネット構築のコードは以下のようになります。

variable "cidr_block" {
    type = map(string)
    default = {
        "1a" = "10.0.0.0/24"
        "1c" = "10.0.1.0/24"
        "1d" = "10.0.2.0/24"
    }
}

resource "aws_subnet" "private" {
    # まず、for_each文を記述
    for_each          = var.cidr_block
    vpc_id            = aws_vpc.main.id
    cidr_block        = each.value
    availability_zone = "ap-northeast-${each.key}"
}

これにより、countと同様に、3つのサブネットを作成することが出来ます。そして、Planを行うと作成されるリソースは、

# aws_subnet.private["1a"] will be created
# aws_subnet.private["1c"] will be created
# aws_subnet.private["1d"] will be created

となり、どのサブネットがどのAZに構築されているのか一目で分かる管理となっています!

forを使用したfor_each

場合によっては、変数cidr_blockは、object型の配列として用意されている可能性もあります。
object型は、map型に様々な型の値を入れることが出来るデータ型になります。今回は文字列型だけになりますが笑。そして、この例だとあまり恩恵がない感じになっていますがそこもご容赦ください・・・。

variable "subnet_variables" {
    type = list(object({
        az_name    = string
        cidr_block = string
    }))
    default = [
        {
            az_name    = "1a"
            cidr_block = "10.0.0.0/24"
        },
        {
            az_name    = "1c"
            cidr_block = "10.0.1.0/24"
        },
        {
            az_name    = "1d"
            cidr_block = "10.0.2.0/24"
        }
    ]
}

このような変数の場合、for文を使用して、mapに変更することが出来ます。
記述方法は以下の通りです。

subnet_info_map = { for l in subnet_variables : l.az_name => l }

これの見方についてです。
まず、:で区切ってください。:の左側がfor文で配列の各要素を取り出しており、右側でsubnet_info_mapのKeyとValueを定義しています。つまり、以下の対応になっています!

subnet_info_map["1a"] = {
    az_name    = "1a"
    cidr_block = "10.0.0.0/24"
}

これを使用したfor_eachは次の通りです。

variable "subnet_variables" {
    type = list(object({
        az_name    = string
        cidr_block = string
    }))
    default = [
        {
            az_name    = "1a"
            cidr_block = "10.0.0.0/24"
        },
        {
            az_name    = "1c"
            cidr_block = "10.0.1.0/24"
        },
        {
            az_name    = "1d"
            cidr_block = "10.0.2.0/24"
        }
    ]
}

resource "aws_subnet" "private" {
    for_each          = { for l in subnet_variables : l.az_name => l }
    vpc_id            = aws_vpc.main.id
    cidr_block        = each.value.cidr_block
    availability_zone = "ap-northeast-${each.value.az_name}"
}

まとめ

countfor_eachどちらを使うべきかは、筆者もまだ勉強中です。。。まずは壁にぶち当たってみて()、どんどんスマートに使い分けて、コード文の記述量・そして管理の負担を少なくしていきましょう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?