インフラ

Terraform の for_each では静的な値を利用する

dai

はじめに

気づけば DWS に入社して 1 年以上が経過しており、時間の経つ速さに驚いているダイです。最近のプロジェクトでは、バックエンド開発に加えて Terraform を用いたインフラ構築も頻繁に行っています。今回は、Terraform の for_each の挙動について、完全に理解していなかった部分があったため、それについてまとめたいと思います。

for_eachを利用したリソースの作成

Terraform でリソースを作成する際、類似のリソースを複数作成すると、似たような resource ブロックを繰り返し記述することになり、コードが冗長になりがちです。


resource "aws_subnet" "subnet1" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.101.0/24"
    availability_zone = "ap-northeast-1a"
    tags = {
        Name = "sample-subnet1"
    }
}

resource "aws_subnet" "subnet2" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.102.0/24"
    availability_zone = "ap-northeast-1c"
    tags = {
        Name = "sample-subnet2"
    }
}

resource "aws_subnet" "subnet3" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.103.0/24"
    availability_zone = "ap-northeast-1c"
    tags = {
        Name = "sample-subnet3"
    }
}

そこで、サブネットの設定情報をローカル変数として定義し、コードの可読性と再利用性を高めます。

locals {
    subnet_map_list = [
        { "name" = "sample-subnet1", "cidr" = "10.0.101.0/24", "az_name" = "ap-northeast-1a" },
        { "name" = "sample-subnet2", "cidr" = "10.0.102.0/24", "az_name" = "ap-northeast-1c" },
        { "name" = "sample-subnet3", "cidr" = "10.0.103.0/24", "az_name" = "ap-northeast-1c" },
    ]
}

resource "aws_subnet" "subnet" {
    for_each = { for subnet in local.subnet_map_list : subnet.name => subnet }

    vpc_id = aws_vpc.vpc.id
    cidr_block = each.value.cidr
    availability_zone = each.value.az_name
    tags = {
        Name = each.key
    }
}

同種のリソースが増えるほど、for_eachで作成する恩恵も大きくなっていきいます。

問題のある動的な値を使用した例

for_eachを使用して作成した上記すべてのサブネットに VPC フローログを設定したいと思います。

resource "aws_flow_log" "subnet_flow_log" {
    for_each = [for s in values(aws_subnet.subnet) : s.id]

    log_destination = aws_s3_bucket.v360_buckets["v360_vpc_flow_logs"].arn
    log_destination_type = "s3"
    traffic_type = "ALL"
    subnet_id = each.key
}

このコードでは、作成するサブネット(aws_subnet.subnet)からサブネットidを取り出し、リスト化することで利用しようとしています。
しかし、実際にterraform plan を実行するとエラーになります。

│ The "for_each" set includes values derived from resource attributes that cannot be determined until apply, and so Terraform cannot
│ determine the full set of keys that will identify the instances of this resource.
│
│ When working with unknown values in for_each, it's better to use a map value where the keys are defined statically in your
│ configuration and where only the values contain apply-time results.
│
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and
│ then apply a second time to fully converge.

エラーメッセージから以下のことがわかります。

  • for_each セットには、apply が行われるまで決定できない値を含めることはできない。
  • for_each で未知の値を扱う際には、キーが設定内で静的に定義されている必要がある。
  • terraform plan 実行時にはまだサブネットが作成されていないため、それを for_each で利用しようとするとエラーが発生する。

問題のない静的な値を使用した例

上記の問題を解決するため、VPC フローログの定義を以下のように修正してみます。

resource "aws_flow_log" "subnet_flow_log" {
    for_each = { for subnet in local.subnet_map_list : subnet.name => subnet }

    log_destination = aws_s3_bucket.v360_buckets["v360_vpc_flow_logs"].arn
    log_destination_type = "s3"
    traffic_type = "ALL"
    subnet_id = aws_subnet.subnet[each.key].id
}

ここでは、local.subnet_map_list から名前をキーとした新しいマップを作成しています。このキーは sample-subnet1 や sample-subnet2 など、静的な値です。したがって、for_each からこれらを安全に利用することができます。

おわりに

Terraform での for_each の利用は、冗長性を排除し、コードの可読性と再利用性を高める大きな利点があります。しかし、そのためには使用法をしっかりと理解し、適切に適用する必要がありそうです。

AUTHOR
dai
dai
記事URLをコピーしました