Terraformの型を整理する

# はじめに
前回はTerraformの環境セットアップに関して簡単に書きました。
[Terraformコトハジメ](https://qiita.com/hikaru_motomiya/items/a358b72d0566f86f6fe7)
今回はTerraformの整理がてら、Variableの型を整理してみました。
~~すっごい地味な記事~~

# まとめ

〇Primitive Types
|型|利用目的|
|:--|:--|
|string|文字を定義したい|
|number|数字を定義したい|
|bool|論理型を定義したい|

〇Complex Types
|type|型|利用目的|
|:--|:--|:--|
|Collection Types|list|連続した値をリストにしたい|
||map|一意のキーにより特定の型のパラメータを管理したい|
||set|ただ単に値を並べたい|
|Structural Types|object|一意のキーにより任意の型のパラメータを管理したい|
||tuple|一意のキーにより任意の型のパラメータをリストにして管理したい|

〇Dynamic Types

|型|利用目的|
|:--|:--|
|any|自動的に最適な型をセットしたい|

# Primitive Types
`string`,`number`,`bool`があります。

## string
読んで字のごとくテキスト用です。

```terraform
variable "var_string" {
    type = string
    default = "string"
}

output "output" {
    value = var.var_string
}
```

出力
``` 
Changes to Outputs:
  + output_string = "string"
```

## number
読んで字のごとく数字用です。

```terraform
variable "var_number" {
    type = number
    default = 1234 
}
output "output_number" {
    value = var.var_number
}
```

出力
```
Changes to Outputs:
  + output_number = 1234
```

## bool
読んで字のごとくbool(論理)用です。

```terraform
variable "var_bool" {
    type = bool
    default = false
}
output "output_bool" {
    value = var.var_bool == true ? "yes" : var.var_bool == false ? "no" : null
}
```

出力
```
Changes to Outputs:
  + output_bool   = "no"
```

`bool`を使って簡単な条件分岐を作れます。
パラメータによって設定値変更したい場合とかに利用します。


# Complex Types
複数の値を1つの値にグループ化する型です。
Collection TypesとStructural Typesの2種類があります。

## Collection Types
`list`と`map`(と`set`)があります。

### list
[0]から始まる連続した整数によって識別される一連の値。
string,number,boolなどを指定できます。

例えば
`list(string)`は文字列のリスト。
`list(number)`は数字のリスト。

```terraform
variable "var_list" {
    type = list(string)
    default = [ "list1","list2","list3" ]  
}
output "output_var_list" {
    value = [for i in var.var_list : i]
}
```
出力
```
Changes to Outputs:
  + output_var_list    = [
      + "list1",
      + "list2",
      + "list3",
    ]
```
### map
keyと呼ばれる文字列により識別される値の集まり。
これも`string`,`number`,`bool`などを指定できます。ただしすべての要素が同じ型である場合のみ。
例えば以下のような指定はできないです。
```terraform
variable "var_map_ng" {
    type = map(number)
    default = {
      "age" = 27
      "name" = "sato"
    }  
}
```

こちらが正しい例。
```terraform
variable "var_map" {
    type = map(string)
    default = {
      "name" = "sato"
      "age" = "27"
    }
  
}
output "output_var_map" {
    value = var.var_map  
}

```
出力
```
Changes to Outputs:
  + output_var_map     = {
      + "age"  = "27"
      + "name" = "sato"
    }
```
### set
`list`のように[0]から始まる整数で管理せず、`map`のように特定のキーで管理されない値の集まり。使いどころが思いつかない。

```terraform
variable "var_set" {
  type = set(string)
  default = [ "aa","bb","cc" ]
}
output "output_set" {
  value = var.var_set
}
```

出力
```
Changes to Outputs:
  + output_set    = [
      + "aa",
      + "bb",
      + "cc",
    ]
```


## Structural Types
一番複雑な型。複数の異なる型の複数の値を1つの値としてグループ化できます。`object`と`tuple`があります。

### object
`map`にそれぞれ別の型を設定できます。

```terraform
variable "var_object" {
    type = object({
        name = string
        age = number
    })
    default = {
      "age" = 27
      "name" = "sato"
    }
}

output "output_var_object" {
    value = var.var_object 
}
```
出力
```
Changes to Outputs:
+ output_var_object  = {
      + age  = 27
      + name = "sato"
    }
```

### tuple
`list`と`object`の組み合わせができます。

```terraform
variable "var_tuple" {
  type = tuple([
    object({
    name = string, age = number
    }),
    object({
    name = string, age = number
    })
  ])
  default = [{
    name = "sato"
    age  = 27
    }, {
    name = "ito"
    age  = 33
  }]
}
```
出力
```terminal
output_var_tuple = [
      + {
          + age  = 27
          + name = "sato"
        },
      + {
          + age  = 33
          + name = "ito"
        },
    ]
```
意図せず`map`ではなく`tuple`になってしまい、`lookup`できずにはまった人は私です。

例えば下記のように`lookup`するとエラーになります。

```terraform
output "output_lookup_tuple" {
  value = lookup(var.var_tuple,"name")
}
```
以下は正しい例。
```terraform
output "output_lookup_tuple" {
  value = lookup(element(var.var_tuple,0),"name")
}
```
出力
```terminal
Changes to Outputs:
  + output_lookup_tuple = "sato"
```



# 番外編：Dynamic Types
`any`を利用すると、指定された値を調べ、自動的に最適な型をセットしてくれます。

```terraform
variable "var_any" {
    type = any
    default = "1234"
}
output "output_var_any" {
    value = var.var_any == 1234 ? "number" :var.var_any == "1234" ?"string" : "bool"  
}
```
```
Changes to Outputs:
  + output_var_any = "string"
```
例だと"1234"というややこしい値を設定していますが、`any`は正しく判断して`string`を選んでいますね。

ではこんなものだとどうなるか。


```terraform
variable "var_map_any" {
    type = map(any)
    default = {
        string_type = "string"
        number_type = 1
        bool_type = true
    }
}
output "output_var_map_any" {
    value = var.var_map_any
}
```

結果は

```
Changes to Outputs:
+ output_var_map_any = [
      + {
          + "bool_type"   = "true"
          + "number_type" = "1"
          + "string_type" = "string"
        },
    ]
```

このように複数の型が異なるであろう値がセットされている場合に、エラーにならず`string`に変換します。
この`string`に変換しようとする挙動によりエラーが発生してしまうことがあるので、`any`の使い方には注意が必要です。個人的には非推奨※。

※typeでの型設定が難儀な場合があり、そういった場合にanyを利用します。

# 参考(公式ドキュメント)

Type Constraints
https://developer.hashicorp.com/terraform/language/expressions/type-constraints