🍫

TerraformのBuilt-in関数や小ネタのあんちょこを作った

に公開

このブログを一言で

TerraformのBuilt-in関数の説明や動作例、小ネタをまとめてみたかったので、重い腰あげてみたよ。
今後も順次メンテしていくよ。

関数(順不同)

参考:Built-in Functions
以下によく使う関数の動作例を記載していきます。

Collection Function

flatten関数

flattenは配列で入れ子になっているものを平準化します。

# flatten
variable "example_list_of_lists" {
  default = [["a", "b"], ["c", "d"], ["e", "f"]]
}

output "as_list_of_lists" {
  value = var.example_list_of_lists
}

output "as_flattened_list" {
  value = flatten(var.example_list_of_lists)
}

Changes to Outputs:
  as_list_of_lists  = [
      [
          "a",
          "b",
        ],
      [
          "c",
          "d",
        ],
      [
          "e",
          "f",
        ],
    ]
  as_flattened_list = [
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
    ]

以下のように、list(object)の場合、env, subnet, ami, instanceのように要素を同じ階層にすることが可能です。

locals {
  default = [
    {
      name     = "dev"
      subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
      ami      = "ami-12345678"
      instance = "t3.micro"
    },
    {
      name     = "prod"
      subnets  = ["10.0.11.0/24", "10.0.12.0/24"]
      ami      = "ami-87654321"
      instance = "t3.small"
    }
  ]

  # flattenで「環境 × サブネット」の組み合わせを1次元にする
  instances = flatten([
    for env in local.default : [
      for sn in env.subnets : {
        env      = env.name
        subnet   = sn
        ami      = env.ami
        instance = env.instance
      }
    ]
  ])
}

output "instances" {
  value = local.instances
}

Changes to Outputs:
  instances = [
      {
          ami      = "ami-12345678"
          env      = "dev"
          instance = "t3.micro"
          subnet   = "10.0.1.0/24"
        },
      {
          ami      = "ami-12345678"
          env      = "dev"
          instance = "t3.micro"
          subnet   = "10.0.2.0/24"
        },
      {
          ami      = "ami-87654321"
          env      = "prod"
          instance = "t3.small"
          subnet   = "10.0.11.0/24"
        },
      {
          ami      = "ami-87654321"
          env      = "prod"
          instance = "t3.small"
          subnet   = "10.0.12.0/24"
        },
    ]

これは、以下のようにfor_eachで利用できます。
for_eachでmapを生成し、EC2を作成することも可能です。

resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "this"
  }
}

resource "aws_subnet" "this" {
  # キーの重複があるため、独自キーを作成して、map化する
  for_each = {
    for v in local.instances : "${v.env}-${v.subnet}" => v
  }

  vpc_id     = aws_vpc.this.id
  cidr_block = each.value.subnet
  tags = {
    Name = "${each.value.env}-subnet"
  }
}

resource "aws_instance" "this" {
  # キーの重複があるため、独自キーを作成して、map化する
  for_each = {
    for v in local.instances : "${v.env}-${v.subnet}" => v
  }

  ami           = each.value.ami
  instance_type = each.value.instance
  subnet_id     = aws_subnet.this[each.key].id

  tags = {
    Name = "${each.value.env}-instance"
  }
}

merge関数

複数のmapを結合して、重複を除いた形で出力します。
複数のKeyが重複している場合は、後続のKeyが優先されます。

variable "map1" {
  default = {
    a = 1
    b = 2
  }
}

variable "map2" {
  default = {
    b = 20
    c = 30
  }
}

output "merged_map" {
  value = merge(var.map1, var.map2)
}

Changes to Outputs:
  merged_map = {
      a = 1
      b = 20
      c = 30
    }

setintersection関数

複数のsetの重複を出力します。

locals {
  list1        = ["a", "b", "c"]
  list2        = ["b", "c", "d"]
  intersection = setintersection(toset(local.list1), toset(local.list2))
}

output "intersection" {
  value = local.intersection
}

Changes to Outputs:
  intersection = [
      "b",
      "c",
    ]

index関数

listの要素番号を返します。

locals {
  my_list = ["a", "b", "c"]
  my_index = index(local.my_list, "b")
}

output "my_index" {
  value = local.my_index
}

Changes to Outputs:
  my_index = 1

keys関数

mapのKeyを返します。

locals {
	example_map = {
		a = 1
		b = 2
		c = 3
	}
	example_keys = keys(local.example_map)
}

output "example_keys" {
	value = local.example_keys
}

Changes to Outputs:
  example_keys = [
      "a",
      "b",
      "c",
    ]

values関数

mapのValueを返します。

locals {
	example_map = {
		a = 1
		b = 2
		c = 3
	}
	example_values = values(local.example_map)
}

output "example_values" {
	value = local.example_values
}

Changes to Outputs:
  example_values = [
      1,
      2,
      3,
    ]

length関数

set, list, mapの長さを返します。

locals {
  my_list = ["a", "b", "c"]
  my_length = length(local.my_list)
}

output "my_length" {
  value = local.my_length
}

Changes to Outputs:
  my_length = 3

Type Conversion Function

toset/tolist関数

for_eachはsetかmapしか受け付けないため、tosetでlistをsetに変換する場合に使われることが多い。逆にtolist関数はsetをlistに変換します。

locals {
  example_set = toset(["a", "b", "c"])
  example_list = tolist(local.example_set)
}

❯ terraform console
> type(local.example_set)
set(string)

> type(local.example_list)
list(string)

IP Network Function

cidrsubnet関数

CIDRを順番に生成する便利な関数。
CIDR計算が不要となる、かつ計算ミスをしなくても良くなるためSubnetを複数作成する場合に便利。
構文はcidrsubnet(prefix, newbits, netnum)。
prefix: ベースとなる CIDR(例: "10.0.0.0/16")
newbits: 追加で借りるビット数(例: 6 → /16 が /22 になる)
netnum: 生成するサブネットの番号(0, 1, 2...)
例:
cidrsubnet("10.0.0.0/16", 6, 0) → 10.0.0.0/22
cidrsubnet("10.0.0.0/16", 6, 1) → 10.0.4.0/22

locals {
  base_cidr = "10.0.0.0/16"
  new_bits  = 6
}
output "subnet_cidr1" {
  value = cidrsubnet(local.base_cidr, local.new_bits, 0)
}

output "subnet_cidr2" {
  value = cidrsubnet(local.base_cidr, local.new_bits, 1)
}

output "subnet_cidr3" {
  value = cidrsubnet(local.base_cidr, local.new_bits, 2)
}

output "subnet_cidr4" {
  value = cidrsubnet(local.base_cidr, local.new_bits, 3)
}

Changes to Outputs:
  subnet_cidr1 = "10.0.0.0/22"
  subnet_cidr2 = "10.0.4.0/22"
  subnet_cidr3 = "10.0.8.0/22"
  subnet_cidr4 = "10.0.12.0/22"

小ネタ

動的にmapを作成する

locals {
  subnets = [
    "10.0.0.0/24",
    "10.0.1.0/24",
    "10.0.2.0/24",
  ]

  azs = [
    "ap-northeast-1a",
    "ap-northeast-1c",
    "ap-northeast-1d",
  ]
}

locals {
  from_az_to_public_subnet = { for i, subnet in local.subnets :
    local.azs[i] => subnet
  }
}

output "test" {
  value = local.from_az_to_public_subnet
}

Changes to Outputs:
  test = {
      ap-northeast-1a = "10.0.0.0/24"
      ap-northeast-1c = "10.0.1.0/24"
      ap-northeast-1d = "10.0.2.0/24"
    }

Availability Zonesに均等にサブネットを配置する例

locals {
	azs = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
	subnets = [
		"10.0.1.0/24",
		"10.0.2.0/24",
		"10.0.3.0/24",
		"10.0.4.0/24",
		"10.0.5.0/24",
		"10.0.6.0/24"
	]
	number_of_azs = length(local.azs)

	subnet_az_map = [
		for subnet in local.subnets : {
			cidr = subnet
			az   = local.azs[index(local.subnets, subnet) % local.number_of_azs]
		}
	]
}

output "subnet_az_map" {
	value = local.subnet_az_map
}

Changes to Outputs:
  subnet_az_map = [
      {
          az   = "ap-northeast-1a"
          cidr = "10.0.1.0/24"
        },
      {
          az   = "ap-northeast-1c"
          cidr = "10.0.2.0/24"
        },
      {
          az   = "ap-northeast-1d"
          cidr = "10.0.3.0/24"
        },
      {
          az   = "ap-northeast-1a"
          cidr = "10.0.4.0/24"
        },
      {
          az   = "ap-northeast-1c"
          cidr = "10.0.5.0/24"
        },
      {
          az   = "ap-northeast-1d"
          cidr = "10.0.6.0/24"
        },
    ]

Discussion