ふり返る暇なんて無いね

日々のメモ書きをつらつらと。メインブログに書くほどでもないことを流してます

豆: Terraformのlocal変数化による繰り返し削減とformat()の使いどころ

TerraformでS3バケットを定義する際、同じ値を複数箇所で使うことがあります。例えば、以下のようなコードです。

resource "aws_s3_bucket" "main" {
  bucket = "${var.env}-${var.component}-${var.service}-app-log"

  tags = {
    Name        = "${var.env}-${var.component}-${var.service}-app-log"
    Environment = var.env
  }
}

この例では、buckettags.Name の両方で同じ文字列を使っており、バケット名を変更したい場合に 2箇所を同時に修正 する必要が出てきてしまいます。こうした繰り返しは、保守性を下げる要因になります。

local変数でまとめて再利用する

このようなケースでは、local値で変数化しておくと便利です。以下のようにすれば、変更箇所を1箇所に集約できます。

locals {
  bucket_name = "${var.env}-${var.component}-${var.service}-app-log"
}

resource "aws_s3_bucket" "main" {
  bucket = local.bucket_name
  tags = {
    Name        = local.bucket_name
    Environment = var.env
  }
}

format()を使って見通しを良くする

上記のような変数展開が複数連なると、見た目がややごちゃごちゃしてきます。その場合は format() 関数を使うことで、よりスッキリと書くことができます。

locals {
  bucket_name = format("%s-%s-%s-app-log", ${var.env}, ${var.component}, ${var.service})
}

複数のバケットを扱いたい場合は?

ここまではバケットが1つの場合でしたが、例えばログ用・データ用など、複数バケットを定義したいケースもよくあります。単純に書くと以下のようになります。

locals {
  app_log_bucket_name = format("%s-%s-%s-app-log", var.component, var.service, each.key)
  app_data_bucket_name = format("%s-%s-%s-app-data", var.component, var.service, each.key)
}

resource "aws_s3_bucket" "app_log" {
  bucket = local.app_log_bucket_name
  tags = {
    Name        = local.app_log_bucket_name
    Environment = var.env
  }
}

resource "aws_s3_bucket" "app_data" {
  bucket = local.app_data_bucket_name
  tags = {
    Name        = local.app_data_bucket_name
    Environment = var.env
  }
}

この方法でも動きますが、バケットの種類が増えるたびにコードがどんどん膨らんでしまいます。

for_each + format()でスケーラブルにする

こうした繰り返しを避けるためには、for_each を使ってループ処理にするのが効果的です。

Terraformでは ループ内でlocal値を定義出来ないので、ループ内で直接 format() を使ってバケット名を構築します。

locals {
  buckets = [ "app-log", "app-data", "some-data1", "some-data2" ]

  bucket_name_format = "%s-%s-%s-%s"
}

resource "aws_s3_bucket" "main" {
  for_each = toset(local.buckets)

  bucket = format(local.bucket_name_format, var.env, var.component, var.service, each.key)
  tags = {
    Name        = format(local.bucket_name_format, var.env, var.component, var.service, each.key)
    Environment = var.env
  }
}

format() の呼び出しが2回出てきて若干冗長に見えるかもしれませんが、共通のフォーマット文字列をlocal値で管理している ため、バケット名の規則を変更したい場合は bucket_name_format を直すだけで済みます。

このようにしておけば、バケットの数が増えてもコードの保守性を高く保ちつつ、Terraform構成をスケーラブルに保てます。


それでは良いTerraformライフを引き続きお過ごしください。

参考