csvdecode — CSVを変数として読み込む

1. 概要

  • csvdecode — CSV文字列をリストのmapに変換
  • CSVファイルから設定を読み込むパターン
  • 実際のユースケース(EC2インスタンス一覧・セキュリティグループルールの管理)

csvdecode(string)はCSV形式の文字列を、ヘッダー行をキーとしたmapのリストに変換します。多数のリソースをCSVで一元管理したい場合に使います。


2. 基本的な使い方

# terraform consoleで確認
> csvdecode("name,age\nAlice,30\nBob,25")
[
  {"age" = "30", "name" = "Alice"},
  {"age" = "25", "name" = "Bob"},
]

1行目はヘッダー(キー名)として扱われ、2行目以降が各行のmapになります。すべての値は文字列として返されます。


3. CSVファイルから読み込む

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"
}

CSVファイル(instances.csv)の内容:

name,instance_type,disk_size
web,t3.micro,20
api,t3.small,30
worker,t3.medium,50
locals {
  # CSVファイルを読み込んでパース
  instances = csvdecode(file("${path.module}/instances.csv"))
  # → [
  #   {name="web", instance_type="t3.micro", disk_size="20"},
  #   {name="api", instance_type="t3.small", disk_size="30"},
  #   {name="worker", instance_type="t3.medium", disk_size="50"},
  # ]

  # for_eachで使うためにname→設定のmapに変換
  instance_map = {
    for inst in local.instances :
    inst.name => inst
  }
}

data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_instance" "servers" {
  for_each      = local.instance_map
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = each.value.instance_type

  root_block_device {
    # CSVの値はすべて文字列なのでtoneumberで変換
    volume_size = tonumber(each.value.disk_size)
    volume_type = "gp3"
    encrypted   = true
  }

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

4. セキュリティグループルールをCSVで管理する

CSVファイル(sg_rules.csv):

port,protocol,cidr,description
80,tcp,0.0.0.0/0,HTTP
443,tcp,0.0.0.0/0,HTTPS
22,tcp,10.0.0.0/8,SSH from internal
locals {
  sg_rules = csvdecode(file("${path.module}/sg_rules.csv"))
}

resource "aws_security_group" "web" {
  name        = "${var.environment}-web"
  description = "${var.environment} web security group"

  dynamic "ingress" {
    for_each = local.sg_rules
    content {
      from_port   = tonumber(ingress.value.port)
      to_port     = tonumber(ingress.value.port)
      protocol    = ingress.value.protocol
      cidr_blocks = [ingress.value.cidr]
      description = ingress.value.description
    }
  }

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

5. csvdecodeの注意点

  • すべての値が文字列として返される。数値が必要な場合はtonumber()で変換する
  • ヘッダー行は必須。ヘッダーなしのCSVは使えない
  • 空のセルは空文字列""になる
  • カンマを含む値はダブルクォートで囲む("value with, comma"

6. 関連記事


7. まとめ

  • csvdecode(str) — CSV文字列をmap of listsに変換。ヘッダー行がキー名になる
  • file()と組み合わせてCSVファイルから設定を読み込める
  • 値はすべて文字列。数値が必要な場合はtonumber()、boolにはtobool()で変換する
  • for_eachと組み合わせてCSVの各行をリソースとして作成できる

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