zipmap / transpose — mapの生成・転置

1. 概要

  • zipmap — 2つのリストからmapを生成
  • transpose — mapのキーと値を入れ替え(転置)
  • 実際のユースケース

zipmap(keys, values)はキーのリストと値のリストを組み合わせてmapを生成します。transpose(map)はmapのキーと値を入れ替えます(値のリストをキー、元のキーを値のリストにする)。


2. zipmap — キーと値のリストからmapを生成

# terraform consoleで確認
> zipmap(["a", "b", "c"], [1, 2, 3])
{"a" = 1, "b" = 2, "c" = 3}

> zipmap(["dev", "stg", "prd"], ["t3.micro", "t3.small", "t3.medium"])
{"dev" = "t3.micro", "prd" = "t3.medium", "stg" = "t3.small"}

リソースIDとリソース名のmapを生成する

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

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

variable "environment" {
  description = "環境名"
  type        = string
  default     = "dev"
}

variable "bucket_names" {
  description = "S3バケット名のリスト"
  type        = list(string)
  default     = ["data", "logs", "backups"]
}

resource "aws_s3_bucket" "buckets" {
  for_each = toset(var.bucket_names)
  bucket   = "${var.environment}-${each.key}"

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

# バケット名 → ARNのmapを生成
locals {
  bucket_arns = zipmap(
    var.bucket_names,
    [for name in var.bucket_names : aws_s3_bucket.buckets[name].arn]
  )
  # → {"data" = "arn:aws:s3:::dev-data", "logs" = "arn:aws:s3:::dev-logs", ...}
}

output "bucket_arns" {
  value = local.bucket_arns
}

3. for式でのzipmap代替パターン

# zipmapと同等の結果をfor式で書く
locals {
  names = ["web", "api", "worker"]
  types = ["t3.micro", "t3.micro", "t3.small"]

  # zipmap版
  instance_types_zip = zipmap(local.names, local.types)

  # for式版(より柔軟)
  instance_types_for = {
    for i, name in local.names :
    name => local.types[i]
  }
}

4. transpose — mapを転置する

transpose(map_of_lists)はmap of listsを転置します。元のmapの値(リスト)の各要素がキーになり、元のキーが値のリストになります。

# terraform consoleで確認
> transpose({"a" = ["1", "2"], "b" = ["1", "3"]})
{"1" = ["a", "b"], "2" = ["a"], "3" = ["b"]}

# サービス → タグのmapをタグ → サービスに転置
> transpose({
    "web" = ["frontend", "public"],
    "api" = ["backend", "public"],
    "db"  = ["backend", "private"]
  })
{
  "backend"  = ["api", "db"],
  "frontend" = ["web"],
  "private"  = ["db"],
  "public"   = ["api", "web"]
}

IAMポリシーのサービス → 許可アクションの転置

variable "service_permissions" {
  description = "サービスと許可するIAMアクションのmap"
  type        = map(list(string))
  default = {
    "web-service"   = ["s3:GetObject", "s3:ListBucket"]
    "api-service"   = ["s3:GetObject", "dynamodb:GetItem", "dynamodb:PutItem"]
    "admin-service" = ["s3:GetObject", "s3:PutObject", "s3:ListBucket", "dynamodb:GetItem"]
  }
}

locals {
  # 転置: アクション → そのアクションを必要とするサービスのリスト
  action_to_services = transpose(var.service_permissions)
  # → {
  #     "s3:GetObject"      = ["admin-service", "api-service", "web-service"],
  #     "s3:ListBucket"     = ["admin-service", "web-service"],
  #     "dynamodb:GetItem"  = ["admin-service", "api-service"],
  #     ...
  #   }

  # s3系アクションを必要とするサービス数を確認
  s3_get_services = lookup(local.action_to_services, "s3:GetObject", [])
}

resource "aws_s3_bucket" "app" {
  bucket = "${var.environment}-app"

  tags = {
    Name        = "${var.environment}-app"
    Environment = var.environment
    ManagedBy   = "terraform"
    AccessCount = tostring(length(local.s3_get_services))
  }
}

5. 関連記事


6. まとめ

  • zipmap(keys, values) — 2つのリストからmapを生成。キーと値のペアをまとめる
  • transpose(map_of_lists) — map of listsを転置。値のリスト要素をキーに、元のキーを値のリストにする
  • zipmapはfor式で代替できるが、シンプルなケースではzipmapの方が読みやすい
  • transposeはIAMポリシーの分析・タグの逆引きなど「どのリソースがこのアクションを使うか」の集計に便利

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