Terraform 小技集

皆さん、こんにちは。技術開発グループのn-ozawanです。
最近、YouTubeでマーモットの動画が流れてきて癒されています。

本題です。
前回までの記事で、Terraformの基本的なことをお話ししました。今日はTerraformで環境を構築する際によく使いそうな便利な小技を紹介します。

関数

小技集に入る前に関数についてお話しさせてください。Terraformには多くの組み込み関数が用意されています。以下は公式サイトに記載されている例です。max関数は受け取った値の中から、最大値となる数値を返却します。

> max(5, 12, 9)
12

Terraformの関数は必ず必要なものではないのですが、Terraformでちょっとしたことをしたい場合にお世話になりますので、覚えといて損はありません。以下に紹介する小技では関数を利用したものになります。

小技集

jsonファイルを参照する方法

環境の情報などをハードコーディングせずに、jsonファイルなどに切り出したいことはあると思います。ここではTerraformでjsonファイルを参照する方法を紹介します。ファイルの内容を取得するにはfile関数を利用します。file関数はファイルの内容を文字列として返却するため、そのままではjsonの中身を扱えません。json形式の文字列からobjectに変換するにはjsondecodeという関数を利用します。

locals {
  # sample.jsonの内容 → {"hello": "world"}
  sample = jsondecode(file("${path.module}/sample.json"))
}

output "console" {
  value = local.sample.hello   # コンソールに"world"が出力される
}

モジュールの引数をチェックする方法

引数を受け取るにはvariableブロックを利用します。variableブロックでは受け取る値の型(StringやNumberなど)を指定することは出来ます。型に加えて、例えば特定の値だけを受け取り、それ以外の値が渡されたらエラーとしたい場合はvalidationを使用します。

variable "az_type" {
  description = "Availability Zone の種類"
  type = string
  validation {
    condition = contains(["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"], var.az_type)
    error_message = "Allowed values for az_type are \"ap-northeast-1a\", \"ap-northeast-1c\", or \"ap-northeast-1d\""
  }
}

上記はAWSのAZ(アベイラビリティーゾーン)の種類を受け取るvariableです。東京リージョンのAZにはap-northeast-1aap-northeast-1cap-northeast-1dの3つがあります。なので、引数として受け取るAZの種類は上記3つに限定して、それ以外はエラーとしています。

validation.conditionには、受け取った値の検証を行います。trueの場合はOKで、falseの場合はNGとしてコンソールにvalidation.error_messageの内容を表示して処理を停止します。

validation.conditionにて、containsという関数が使われています。第2引数の値が、第1引数のリストに存在する場合はtrue、存在しない場合はfalseを返却します。

モジュールの引数(配列)をチェックする方法

では、その引数が配列の場合はどうやってチェックするのでしょうか。答えは簡単でfor文で回せば出来ます。

variable "az_type" {
  description = "Availability Zone の種類"
  type = list(string)
  validation {
    condition = alltrue([
      for az in var.az_type : contains(["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"], az)
    ])
    error_message = "Allowed values for az_type are \"ap-northeast-1a\", \"ap-northeast-1c\", or \"ap-northeast-1d\""
  }
}

for文で受け取った配列を回し、要素ごとにcontains関数でチェック、booleanの配列に変換します。alltrue関数は、引数の配列要素がすべてtrueの場合にtrueを返却します。1つでもfalseが含まれている場合はfalseを返却します。

複数リソースの特定の値を配列にまとめる方法

for_eachなどにより、リソースを複数作成することが出来ます。その作成された複数のリソースから、特定の値を配列として取得したいケースがあります。

例えばAWSでRDS(DB)を構築する際、配置先のSubnetを指定するためにDB Subnet Groupというリソースを作成します。RDSは複数のSubnetを跨いで配置※することが出来るため、SubnetのIDを配列で指定します。
※正確には、RDSを冗長化するためにAZを跨いで配置する、が正しいです。AZごとにPrivate Subnetを作成する必要があり、東京リージョンであれば最大3つのSubnetを作成することになります。

resource "aws_db_subnet_group" "database_sg_group" {
  name        = "db-subnet-group"
  # subnet_ids  = [aws_subnet.private["1a"].id, aws_subnet.private["1c"].id, aws_subnet.private["1d"].id]
  subnet_ids  = [for subnet in aws_subnet.private : subnet.id]
}

resource "aws_db_subnet_group"subnet_idsで、作成した複数のリソースをfor文で処理して、Subnet IDの配列に変換しています。コメント分はfor文を利用しなかった場合のやり方です。for文を利用した場合と比べ、記述内容が冗長であり、subnetの個数の変更に弱い記述となっています。特別な理由がない限りはfor文を利用した方がいいでしょう。

おわりに

いかがでしたでしょうか。Terraformの小技集を紹介しました。小技と言っても、実際にTerraformで環境を構築しようとすると、よく使うかと思います。

例えばAWS IAMではポリシーをJSON形式で指定しますので、ポリシーをtfファイルにハードコーディングするのではなくjsonファイルで管理するのも良いでしょう。Object型からList型への変換にfor文を利用する方法についても、可読性が上がるだけでなく、本番環境や検証環境などの違いにも柔軟に対処できます。

ではまた。

Recommendおすすめブログ