toset / tolist / tomap — list・set・mapを変換する型変換関数

1. 概要

  • toset — listやtupleをset型に変換する
  • tolist — setやtupleをlist型に変換する
  • tomap — objectをmap型に変換する
  • 型変換が必要な場面とfor_eachとの関係

Terraformの型システムでは、setlistmapは似ているようで異なります。for_eachはset/mapしか受け付けないため、listを渡すときはtosetが必要です。


2. toset — listをsetに変換する

toset(list)は、listをsetに変換します。setは順序がなく、重複を持てない型です。

# terraform consoleで確認
> toset(["dev", "stg", "prd"])
toset([
  "dev",
  "prd",
  "stg",
])

# 重複は自動的に排除される
> toset(["dev", "stg", "dev", "prd"])
toset([
  "dev",
  "prd",
  "stg",
])

for_eachにlistを渡すにはtosetが必要

# ❌ for_eachにlistは渡せない
resource "aws_s3_bucket" "env" {
  for_each = ["dev", "stg", "prd"]  # Error: for_each supports sets and maps
  bucket   = "myapp-${each.key}"
}

# ✅ tosetでsetに変換してから渡す
resource "aws_s3_bucket" "env" {
  for_each = toset(["dev", "stg", "prd"])
  bucket   = "myapp-${each.key}"

  tags = {
    Name        = "myapp-${each.key}"
    Environment = each.key
    ManagedBy   = "terraform"
  }
}

tosetを使う完全な例

terraform {
  required_version = ">= 1.9"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

variable "environments" {
  description = "作成する環境名のリスト"
  type        = list(string)
  default     = ["dev", "stg", "prd"]
}

resource "aws_s3_bucket" "env_bucket" {
  # variableはlist型なのでtosetで変換
  for_each = toset(var.environments)

  bucket = "myapp-${each.key}-assets"

  tags = {
    Name        = "myapp-${each.key}-assets"
    Environment = each.key
    ManagedBy   = "terraform"
  }
}

3. tolist — setをlistに変換する

tolist(set)は、setをlistに変換します。順序が保証されない点に注意が必要です。

# terraform consoleで確認
> tolist(toset(["c", "a", "b"]))
["a", "b", "c"]  # アルファベット順に並び替えられる(保証はされない)

インデックスアクセスに使う

setはインデックスアクセスができないため、特定の要素が必要なときはtolistで変換します。

data "aws_availability_zones" "available" {
  state = "available"
}

# setはインデックスアクセスできないのでtolistに変換
locals {
  first_az  = tolist(data.aws_availability_zones.available.names)[0]
  second_az = tolist(data.aws_availability_zones.available.names)[1]
}

resource "aws_subnet" "primary" {
  vpc_id            = aws_vpc.main.id
  availability_zone = local.first_az
  cidr_block        = "10.0.1.0/24"

  tags = {
    Name      = "primary-subnet"
    ManagedBy = "terraform"
  }
}

4. tomap — objectをmapに変換する

tomap(object)は、objectをmap型に変換します。objectはキーと値の型が固定ですが、mapは同じ値型の任意のキーを持てます。

# terraform consoleで確認
> tomap({ name = "web", port = 80 })
{
  "name" = "web"
  "port" = "80"  # 数値も文字列に統一される(mapは同一型)
}

型制約でmapを明示する

variable "tags" {
  description = "リソースに付与するタグ"
  type        = map(string)
  default = {
    Environment = "dev"
    ManagedBy   = "terraform"
  }
}

resource "aws_s3_bucket" "main" {
  bucket = "myapp-main"

  tags = merge(var.tags, {
    Name = "myapp-main"
  })
}

5. list / set / map の違いまとめ

順序重複インデックスアクセスfor_each用途
listありlist[0]❌(tosetが必要)順序付きのコレクション
setなし不可重複なし、for_eachに渡す
mapなし値は可map["key"]キー→値の対応

6. よくある間違い

for_eachにlist変数を直接渡す

variable "bucket_names" {
  type    = list(string)
  default = ["bucket-a", "bucket-b"]
}

# ❌ listはfor_eachに渡せない
resource "aws_s3_bucket" "this" {
  for_each = var.bucket_names  # Error!
  bucket   = each.value
}

# ✅ tosetで変換する
resource "aws_s3_bucket" "this" {
  for_each = toset(var.bucket_names)
  bucket   = each.key
}

setに対してインデックスアクセスする

# ❌ setはインデックスアクセスできない
output "first_subnet" {
  value = aws_subnet.this.*.id[0]  # setに[0]は使えない
}

# ✅ tolistに変換してからアクセスする
output "first_subnet" {
  value = tolist(values(aws_subnet.this))[0].id
}

7. 関連記事


8. まとめ

  • toset(list) — listをsetに変換。重複を排除。for_eachにlistを渡すときに必須
  • tolist(set) — setをlistに変換。インデックスアクセスが必要なときに使う
  • tomap(object) — objectをmapに変換。値型が統一される
  • for_eachはset/mapしか受け付けないため、list変数を持つ場合はtosetで変換する

動作確認バージョン: Terraform >= 1.9 公式ドキュメント: https://developer.hashicorp.com/terraform/language/functions/toset