TerraformのLocal Valuesについてよくわからなかったので検証してみた

## Local Valuesとは

公式によると、

- local values というものを普通のプログラミング言語に照らし合わせて考えると、、、
    - `variable` が関数のargumenｔに相当
    - `output` が関数のreturn valueに相当
    - では `locals(=local variables)` は・・・というと関数内スコープの変数に相当

・・・だそうですがよくわからなかったのでいろいろ検証したり調べたりしてみました。

ちなみに `Local Values` であり、 `Local Variables` ではありません。

## 検証したりドキュメントを読んで理解したこと

（自身が理解した内容の範囲ですが）結論から書くと、

1. variableブロックのdefault値には [Interpolation Syntax](https://www.terraform.io/docs/configuration-0-11/interpolation.html) が使えないが、localsブロックではそれが可能
2. 影響範囲/利用範囲がモジュール内のスコープにとどまる
    - 例えば作業ディレクトリのtfファイル間では共用可能だが、moduleに同名の変数が合ってもそれは利用できない・・・というような
3. `-var` 等により外部から与えることは出来ない


では、１つずつ見ていきます。

## 1. localsでは [Interpolation Syntax](https://www.terraform.io/docs/configuration-0-11/interpolation.html) が使える

### local values を使った場合

例えば、以下のような内容は成立します。

main.tf

```bash
variable "is_prod" {
  type    = "string"
  default = "false"
}

locals {
  keyname = "${var.is_prod ? "prod-key": "stage-key"}"
}

resource "ecl_compute_keypair_v2" "keypair_1" {
  name = "${local.keyname}-1"
}
```

この状態で、 `is_prod` を `true` にするとこうなります。

```bash
% terraform apply -var 'is_prod=true'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ecl_compute_keypair_v2.keypair_1
      id:          <computed>
      fingerprint: <computed>
      name:        "prod-key-1"
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

逆に、 `is_prod` を `false` にする（というか与えない）とこうなります。

```bash
% terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ecl_compute_keypair_v2.keypair_1
      id:          <computed>
      fingerprint: <computed>
      name:        "stage-key-1"
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

localsに書いた3項演算子が正しく動作していることがわかると思います。

### variableを使った場合

もしこれと同じことをvariableでやるとエラーになります。

```bash
variable "is_prod" {
  type    = "string"
  default = "false"
}

variable "keyname" {
  type    = "string"
  default = "${var.is_prod ? "prod-key": "stage-key"}"
}

resource "ecl_compute_keypair_v2" "keypair_1" {
  name = "${local.keyname}-1"
}
```

上記の様なtfファイルを利用して `terraform apply` すると以下のようなエラーとなります。

```bash
% terraform apply

Error: variable "keyname": default may not contain interpolations
```

## 2. 影響範囲がモジュールスコープにとどまる

以下のようなディレクトリ構造を考えます。

```bash
.
├── main.tf
├── modules
│   └── main.tf
└── provider.tf <-- 認証系の情報はココに書いてあると仮定して説明は割愛
```

ファイルの内容は以下のようになっています。

### main.tf

```go

module "keypair" {
  source = "./modules"

  is_prod = "${var.is_prod}"
}

variable "is_prod" {
  type    = "string"
  default = "false"
}

locals {
  "keyname" = "${var.is_prod ? "prod-key-main" : "stage-key-main"}"
}

resource "ecl_compute_keypair_v2" "keypair_1_main" {
  name = "${local.keyname}"
}
```

### modules/main.tf

```bash
variable "is_prod" {
  type    = "string"
  default = "false"
}

locals {
  "keyname" = "${var.is_prod ? "prod-key-module" : "stage-key-module"}"
}

resource "ecl_compute_keypair_v2" "keypair_1_module" {
  name = "${local.keyname}"
}
```

ポイントは、

- `main.tf` にも、 `modules/main.tf` にも、同名の `locals.keyname` が存在し、以下のように挙動に差分がある
    - `main.tf` の方は、 `var.is_prod` の値に応じて、 `prod-key-main` or `stage-key-main` を返す処理が書いてある
    - `modules/main.tf` の方は、 `var.is_prod` の値に応じて、  `prod-key-module` or `stage-key-module` を返す処理が書いてある
    - `is_prod` は `main.tf` から `modules/main.tf` に伝搬するようになっている

です。

この状態で、 `terraform apply` します。

### `is_prod` が `false` の場合の `terraform apply` 結果

```
% terraform apply -var 'is_prod=false'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ecl_compute_keypair_v2.keypair_1_main
      id:          <computed>
      fingerprint: <computed>
      name:        "stage-key-main"  <-- main.tf側のlocals.keynameの処理結果になっている
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>

  + module.keypair.ecl_compute_keypair_v2.keypair_1_module
      id:          <computed>
      fingerprint: <computed>
      name:        "stage-key-module"  <-- module/main.tf側のlocals.keynameの処理結果になっている
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

### `is_prod` が `true` の場合の `terraform apply` 結果

```
% terraform apply -var 'is_prod=true'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ecl_compute_keypair_v2.keypair_1_main
      id:          <computed>
      fingerprint: <computed>
      name:        "prod-key-main"  <-- main.tf側のlocals.keynameの処理結果になっている
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>

  + module.keypair.ecl_compute_keypair_v2.keypair_1_module
      id:          <computed>
      fingerprint: <computed>
      name:        "prod-key-module"   <-- module/main.tf側のlocals.keynameの処理結果になっている
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

このようにmodule側に同名の関数が存在しても、処理が独立して行われているのがわかります。
まさにlocalスコープの関数のようなイメージですね。

### 3. `-var` 等により外部から与えることは出来ない

言わずもがなですがやってみます。 :sweat: 

今度は `main.tf` 単体で事が足ります。

```bash
locals {
  keyname = ""
}

resource "ecl_compute_keypair_v2" "keypair_1_main" {
  name = "${local.keyname == "" ? "empty" : "specified" }"
}
```

ここで `terraform apply` 。

```bash
% terraform apply -var 'keyname=localkeyname'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + ecl_compute_keypair_v2.keypair_1_main
      id:          <computed>
      fingerprint: <computed>
      name:        "empty" <-- 付与された扱いになっていない
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

ということで外から与えられませんよーということですね。

以上、 `local values` について調べてみました。