[IaCを学ぼう第3回] Terraform(HCL)における変数の使い方

## これは何？
IaCを学ぼうの第3回、Terraform(HCL)を使う場合の変数の概要と使い方を説明する記事です。

## ちょっとだけ前回(第2回)の復習
前回は以下のような`resource`と呼ばれるものを記述しVPCを作成したと思います。

```HCL
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "test-vpc"
  }
}
```

今回はこのコードを更に利便性を高くするためにテコ入れしていきます。

## どういうこと？
`cidr_block`と`tags`の中の`Name`の部分を見てみましょう。
今はそれぞれ`10.0.0.0/16`と`test-vpc`という値がコードに直接記述されています。
これでももちろん動作はしますが、通常はこの値が構築するインフラの要件によって変わりますよね。
記述したコードは基本複数のプロジェクトで使いまわされることが多いです。
毎回同じ内容を記述するのは時間がかかりますよね。

そんなときに値が変更になる部分は予め変数化しておき、必要な値をその都度代入出来るようにしておくと大変便利です。

## 変数の定義
プログラミングにおいても言語毎に定義のやり方はいろいろありますが
Terraformの場合は`variable`として表現します。
書式は以下です。

```HCL
variable "変数名" {
  type      = 変数の型
  default   = デフォルト値
  sensitive = 値を秘匿化するかどうか
}
```

ひとまず何らかの文字列を格納する変数を定義する場合は以下のように記述します。

```HCL
variable "string" {
  type      = string
  default   = "sato"
  sensitive = false
}
```

## 変数の参照
`var.変数名`という書き方をします。
`aws_vpc`の`cidr_block`を変数化してみると以下のようになります。

```HCL
variable "cidr_block" {
  type      = string
  default   = "10.0.0.0/16"
  sensitive = false
}

resource "aws_vpc" "vpc" {
  cidr_block = var.cidr_block

  tags = {
    Name = "test-vpc"
  }
}
```

## 変数に対する値の代入

デフォルト値を記述しておけばひとまずその値が格納された変数を定義出来ますが
Terraformでは複数のやり方で変数に値を代入することができます。

- コマンドラインで対話的に入力
- terraformコマンド実行時のオプションで指定
- terraform.tfvarsファイルを利用
- 環境変数に値を設定する

### 対話的に入力

以下のようにデフォルト値を指定しない状態で変数の定義のみやっておくと

```HCL
variable "string" {
  type      = string
  sensitive = false
}
```

例えば`terraform plan`を実行してみると対話的に値を入力して実行できます。
```
$ terraform plan
var.string
  Enter a value: sato
```

### コマンド実行時のオプションで指定

変数の定義は同様で、以下のようにコマンド実行時の`-var`オプションで値を代入できます。

```
$ terraform plan -var 'string=sato'
```

### terraform.tfvarsファイルを利用

`terraform.tfvars`という変数の値が記述されたファイルを利用することも出来ます。
この方法が変数の代入で一番利用する頻度が多いと思います。

```HCL:main.tf
variable "string" {
  type      = string
  sensitive = false
}
```

```HCL:terraform.tfvars
string = "sato"
```

Terraformを実行するディレクトリにtfファイル群と別に`terraform.tfvars`という名前で一緒に置いておくと
コマンド実行時に自動で読み込んでくれます。

ちなみにコマンド実行時のオプションとして`-var-file`があり
これを使うと別のtfvarファイルも読み込むことが可能です。

```
$ terraform plan -var-file=other.tfvars
```

### 環境変数に値を設定する

exportコマンド等で環境変数にセットした値を利用することも可能です。
環境変数名は`TF_VAR_変数名`という形にしておく必要があります。

```
export TF_VAR_string="sato"
```

## 変数のデータ型

プログラミング言語と同様に扱うデータによって変数の型が存在します。

### 文字列(string)

```HCL
variable "string" {
  type      = string
  sensitive = false
}
```

値の参照は`var.変数名`です。

```HCL
param = var.string
```

### 数値(number)

```HCL
variable "number" {
  type      = number
  default   = 10
  sensitive = false
}
```

値の参照は`var.変数名`です。

```HCL
param = var.number
```

### 真偽(bool)

```HCL
variable "bool" {
  type    = bool
  default = true
}
```

値の参照は`var.変数名`です。

```HCL
param = var.bool
```

### キーバリュー(object)

```HCL
variable "object" {
  type = object({
    name = string
    age  = number
  })
  default = {
    name = "sato"
    age  = 21
  }
}
```

値の参照は`var.変数名.キー名`です。
`name`を参照する場合は以下のようになります。

```HCL
param = var.object.name
```

### 各列の型が異なる配列(tuple)

```HCL
variable "tuple" {
  type = tuple([
    string, number
  ])
  default = ["sato", 21]
}
```

値の参照は`var.変数名[要素番号]`です。
`sato`を参照する場合は以下のようになります。

```HCL
param = var.tuple[0] //21を参照する場合は[1]となります
```

### 各列の型が同じ配列(list)

```HCL
variable "list" {
  type    = list(string)
  default = ["sato", "tanaka"]
}
```

値の参照は`var.変数名[要素番号]`です。
`sato`を参照する場合は以下のようになります。

```HCL
param = var.list[0] //tanakaを参照する場合は[1]となります
```

配列と言えば繰り返し構文ですよね。
Terraformにも`count`や`for_each`があるので別記事になりますがまとめていきます。

### キーが文字列の連想配列(map)

```HCL
variable "map" {
  type = map(number)
  default = {
    "sato"   = 21
    "tanaka" = 22
  }
}
```

値の参照は`var.変数名.キー名`です。

```HCL
param = var.map.sato //値としては21というnumberが返ります
```

### 値の重複がない配列(set)

```HCL
variable "set" {
  type = set(string)
  default = [
    "sato",
    "tanaka",
    "sato",
    "tanaka"
  ]
}
```

値の参照は`var.変数名`です。

```HCL
param = var.set
```

Terraformでは`output`という記述ができます。
以下のコードで`set`という変数の値を出力してみると

```HCL
output "set" {
  value = var.set
}
```

```
Changes to Outputs:
  + set = [
      + "sato",
      + "tanaka",
    ]
```

重複した値が除かれた状態で参照出来ていますね。

## 内部変数もある

`variable`とは別に`locals`というものも存在します。
`variable`と異なり外部から値の上書きができませんが
変数定義の際に計算式を記述出来たりするのでとても便利です。

```HCL
variable "environment" {
  type   = string
  default = "dev"
}

locals {
  instance_type = var.environment == "dev" ? "t3.micro" : "m6i.large"
}
```

`local.変数名`で変数の値を参照できます。

```HCL
output "instance_type" {
  value = local.instance_type
}
```

## まとめ
次回は第4回ということで`resource`間のリソースIDなどの動的参照について記事を書きます。
変数は定義・代入・参照を繰り返して多様するので覚えておきましょう。