TerraformドキュメントのConfiguration Languageについて  〜0.11を振り返ってみる〜

## はじめに

Terraform 0.12がリリースされましたが、ではどこが変わったのか・・・を調べていく過程で、そもそも0.11時代ってどうだったんだろう・・・というのをちゃんと把握したく、一度全部読んでみました。
その内容をかいつまんで記載したのが以下です。

改めて読んでみると知らなかったことがいろいろありました。
あと、まとめておくと、自分が読み直すときに思い出しやすいかなと。

まずは [Terraform CLI](https://www.terraform.io/docs/cli-index.html) の内容を理解すべく読み進めているのですが全部を一度に理解していくのは厳しいので [Configuration Language](https://www.terraform.io/docs/configuration/index.html) セクション配下の内容についてまずは触れます。
（このセクションだけバージョンごとに別れていたので、まずはここを読み直すのがいいかなという考え。それ以外の差分も調べておかないとですが・・・ :sweat: ）

- 0.11 and Older : https://www.terraform.io/docs/configuration-0-11/resources.html
- 0.12 and Newer : https://www.terraform.io/docs/configuration/index.html

## TL; DR

ちと長いっす。

## [0.11 and Older](https://www.terraform.io/docs/configuration-0-11/index.html)

- まずは0.11までの内容について以下記載します
- ところどころ `()` 内に、自分なりの検証結果や意見を書いてます
- たまに `()` じゃないけど自分なりの調べ結果になっていたりもします
- Terraformサイト側とほぼ同じ階層構造で本ページは書いていますが、ずれているところもあります。各セクションで本家へのリンクを張っているので詳細はそちらも参照ください

### [Load Order and Semantics](https://www.terraform.io/docs/configuration-0-11/load.html)

- コンフィグは、ファイルのフォーマットに応じて .tf もしくは .tf.json に記載する
- 作成されたディレクトリ内のコンフィグはアルファベット順で呼び出される。なお上記以外の拡張子のファイルは無視される
- それぞれのファイルはマージされるのではなくて、結合される。なので同一名のファイルなどはバリデーションエラーを引き起こす
    - Overrideファイルは例外。これは上記の読み込み後、アルファベット順で呼ばれる
    - また挙動的にもマージされる形になる

### [Configuration Syntax](https://www.terraform.io/docs/configuration-0-11/syntax.html)

- Terraformの設定は[HashiCorp Configuration Language (HCL)](https://github.com/hashicorp/hcl)]により行う

- machine-friendlyフォーマットとして、JSONも読むことは可能になっている
- HCLの基本的な文法は以下
    - 一行コメントは `#`で開始
    - 複数行コメントは、 `/* */` で挟む
    - 値の代入は `key = value` 形式
        - 空白は影響を与えない
        - プリミティブ型(文字列、数値、論理型)やlist, mapが指定可能
            - （検証結果より)ただし、variableのtypeにbooleanは使えない。こんなエラーになる

            ```go
            Variable '...' type must be one of [string, map, list] - 'boolean' is not a valid type
            ```
            - 本ページの後半の `Variables` 節でも記載
        - 文字列はダルルクオートで囲む
        - 文字列は `${}` を使って補間することもできる。詳しくはその他含めて[こちら](https://www.terraform.io/docs/configuration-0-11/interpolation.html)を参照
        - 複数行文字列は `<<EOF`から開始し `EOF` まで、いわゆる `here doc` 形式で記載可能
        - 数値は基本的に10進数だが、`0x` で開始すると16進法で記載することも可能
        - 論理型は`true`もしくは`false`
        - `[]` でリストを表現
        - マップは `{}`と`:`で定義する（と書いてあるが・・・JSONの場合だと思われ）
          - キーのクォートは、キーが数値で開始する場合以外は無視される
          - 1行にマップを記載する場合はkey/valueペアの後のカンマが必要だが、複数行にkey/valueを記載していけばその必要はない
          - （ここはわかりにくかったので自分なりに調べた）
              - まず`:` って使えないようにしか見えない(Terraformがinvalid charを返すので)。実際には`key = value` 形式。これは1行記載でも複数行記載でも同じ
              - 1行に複数key/valueを書く場合に、`tags = { foo = "bar", bar = "baz" }`はOK
              - 複数行にkey/valueを書く場合以下のようにすればいいという意味（以下の２つは同義）

                  ```go
                  tags = {
                    foo = "bar"
                    bar = "baz"
                  }
                  ```

                  ```go
                  tags = {
                    foo = "bar"
                  }

                  tags = {
                    foo = "bar"
                  }
                  ```

              - (ちなみにvscodeで自動フォーマットをした場合、key/valueの間に空行が1行できるが、それは削除しても問題なし)


### [Interpolation Syntax](https://www.terraform.io/docs/configuration-0-11/interpolation.html)
- 基本的に `${}`を利用して文字列の補間が可能
- [Available Variables](https://www.terraform.io/docs/configuration-0-11/interpolation.html#available-variables)
    - 文字列を使う場合 `${var.foo}` のような形
    - マップを使う場合、 `${var.amis["us-east-1"]]}` のように使う。ここでは`amis`がマップ定義された変数で、そのキーが`us-east-1`であると仮定
    - リストを使う場合、`${var.subnets[idx]}`のように使う。ここでは `idx`がリストのインデックスを意味する。idxにクォートはいらない
    - [Attributes of your own resource](https://www.terraform.io/docs/configuration-0-11/interpolation.html#attributes-of-your-own-resource)
        - **Provisionerの場合のみ**だが、 `${self.private_ip}` のようにすることで、独自リソースの属性参照が可能（**Provider**の場合はここは関係がない)
    - [Attributes of other Resources](https://www.terraform.io/docs/configuration-0-11/interpolation.html#attributes-of-other-resources)
        - 他のリソースの属性値を参照したい場合 `TYPE.NAME.ATTRIBUTE` という形式で書く
            - (これによって、暗黙的な依存関係が定義される)
            - これとは別に明示的な依存関係を定義したい場合は、 [こちら](https://www.terraform.io/docs/configuration-0-11/resources.html#explicit-dependencies) を参照
        - 例として、 `${aws_instance.web.id}` のような記載になる
        - count等で一括で作成されたリソースの属性を参照する場合、 `${aws_instance.web.0.id}` のように書く
        - `splat syntax` も使える
            - （自分なりの検証から）
                - 例えばinstanceをcount=N(N>1)で作成した場合等に、そのIDを取得したい場合、以下のように参照することが出来る
                - 一方、varなどの参照時には使えないように見えるので、どなたかやり方ご存知のかたいらっしゃいましたら教えてください :bow: 

                ```go
                resource "ecl_compute_instance_v2" "instance_1" {
                  count     = 2
                  name      = "myinstance01"
                  flavor_id = "..."
                  image_id  = "..."
                  ...
                }
      
                output "my_ids" {
                  value = "${ecl_compute_instance_v2.instance_1.*.id}"
                }
                ```

    - [Outputs from a module](https://www.terraform.io/docs/configuration-0-11/interpolation.html#outputs-from-a-module)
        - `MODULE.NAME.OUTPUT` で記載する。例えば `${module.foo.bar}`
    - [Count information](https://www.terraform.io/docs/configuration-0-11/interpolation.html#count-information)
        - 自分自身がカウント付きで定義されているリソースのような場合、そのインデックスを `${count.index}` で参照できる

        ```go
        resource "ecl_compute_instance_v2" "instance_1" {
          count     = 2
          name      = "myinstance-${count.index}" // myinstance-1, myinstance-2
          flavor_id = "..."
          image_id  = "..."
          ...
        }
        ```

    - [Path information](https://www.terraform.io/docs/configuration-0-11/interpolation.html#path-information)
        - `path.TYPE` という形でパス情報を埋め込める。TYPEに利用可能なのは、 `cwd` , `module` , `root` のいずれか
    - [Terraform meta information](https://www.terraform.io/docs/configuration-0-11/interpolation.html#terraform-meta-information)
        - 現在実行中のTerraformに関するmeta情報の取得ができる
        - 現在取得可能なのは terraform envの情報だけ(なのだが、これ自体がdeprecateでありworkspaceに移行推奨。あまり使うこともないかも）
- [Conditionals](https://www.terraform.io/docs/configuration-0-11/interpolation.html#conditionals)
    - 要するに3項演算子が使える　`CONDITION ? TRUEVAL : FALSEVAL`
- [Built-in Functions](https://www.terraform.io/docs/configuration-0-11/interpolation.html#built-in-functions)
    - ここは長いので割愛
- [Templates](https://www.terraform.io/docs/configuration-0-11/interpolation.html#templates)
    - Data sourceとほぼ同等の使用感でTemplateが使える
    - （Note欄に記載のあるように、テンプレートを別ファイルに **分けずに** 使う場合以下のように変数埋め込み箇所を `$${xxx}` としておく必要がある）

        ```go
        data "template_file" "example" {
          template = "$${hello}$${world}"

          vars {
            hello = "goodnight"
            world = "moon"
          }
        }

        resource "ecl_compute_instance_v2" "instance_1" {
          count     = 1
          name      = "${data.template_file.example.rendered}" // <-- goodnightmoon
          flavor_id = "..."
          image_id  = "..."
        ```
- [Math](https://www.terraform.io/docs/configuration-0-11/interpolation.html#math)
    - 基本的な四則演算 + `%(剰余)` が使える

### [Overrides](https://www.terraform.io/docs/configuration-0-11/override.html)

- Terraformが `.tf`, `.tf.json` を結合した後、事後にマージ可能なコンテンツを定義するための方法
- ファイル名は以下のようにしておく必要がある
    - `override.tf` もしくは `override.tf.json` 
    - `xxx_override.tf` のように `_override` を含む
- このoverrideファイル群もアルファベット順で読み込まれるので、例えば以下のケースは最終的に `count = 3` が適用される（つまりcount =2, 3の記載を逆にすると、count=2が適用される)
    - main.tfを以下のように定義する

        ```go
        resource "ecl_compute_instance_v2" "instance_1" {
          count     = 1
          name      = "instance_1"
          flavor_id = "..."
          image_id  = "..."

        }
        ```

    - 00_override.tf(最初に呼ばれるoverrideファイル)を以下のように定義する

        ```go
        resource "ecl_compute_instance_v2" "instance_1" {
          count     = 2
       }
        ```

    - 01_override.tf(次に呼ばれるoverrideファイル)を以下のように定義する

        ```go
        resource "ecl_compute_instance_v2" "instance_1" {
          count     = 3
       }
        ```

### [Resources](https://www.terraform.io/docs/configuration-0-11/resources.html)

- 一番使うであろう機能
- 定義の仕方は以下の通り
    - `resource` ブロックにより定義する
        - その後ろに `TYPE` , `NAME` を付与
        - `TYPE` , `NAME` の組み合わせがtfファイル内でユニークでなくてはならない
        - `resource` ブロックは `{}` で定義し、その中にリソースの設定を行うが、定義可能な情報を定義するのはproviderの役目
    - [Meta-parameters](https://www.terraform.io/docs/configuration-0-11/resources.html#meta-parameters)
        - すべてのリソースにおいて共通的に利用可能なパラメータ群は以下の通り
            - count
                - リソースが作成される数を定義する
                - モジュール内では利用することが出来ない
            - depends_on
                - 明示的な(個人的には強制的な・・・がしっくりくる）依存関係定義において利用する
                - 詳しくは [こちら](https://www.terraform.io/docs/configuration-0-11/resources.html#explicit-dependencies)
            - provider
                - プロバイダを利用可能にするための定義を行う
                    - providerブロックにおいてエイリアス(TYPE.ALIAS(例: asw.west))を利用することで、同一のプロバイダに対して複数の認証情報等をもたせることができる
                    - リソースをそちらの `エイリアスされた` プロバイダ側で利用したい場合などにこのパラメータを利用する
                - [こちら](https://www.terraform.io/docs/configuration-0-11/resources.html#multiple-provider-instances) がわかりやすい
                - （本ページのもう少し下の方にも書いてある）
            - lifecycle
                - リソースの振る舞いを定義するためのパラメータブロック
                - キーとして以下を使用可能
                    - create_before_destroy(bool)
                        - trueにセットすると、リソースのdestroyを伴う変更(=force new)のときに、先にリソースを作ってから削除するようになる
                    - prevent_destroy(bool)
                        - リソースの保護のためのパラメータ
                        - trueにセットすると、 `terraform destroy` を行った際にエラーとなる
                    - ignore_changes(list of strings)
                        - このパラメータに対し、文字列のリスト形式で `変更を無視するパラメータ名` を付与した場合、`.tf` 上で当該パラメータを変更しても `terraform apply` 時に無視される
                    - 例：
    
                        ```go
                        resource "ecl_compute_instance_v2" "instance_1" {
                          count     = 1
                          name      = "instance_1_edited" // <-- 変更が反映されない
                          flavor_id = "..."
    
                          lifecycle {
                            create_before_destroy = false
                            ignore_changes        = ["name"]  // <-- name変更を無視                        
                          }
                        }
                        ```
    
    - [Timeouts](https://www.terraform.io/docs/configuration-0-11/resources.html#timeouts)
        - リソースの `create` . `update` , `delete` 時のタイムアウトを定義する
        - 単位は `s(seconds)` . `m(minutes)` , `h(hours)` が利用可能
        - デフォルト値はprovider側のリソース定義において指定されているが、 `.tf` 側でそれの上書きをするイメージ
    - [Explicit Dependencies](https://www.terraform.io/docs/configuration-0-11/resources.html#explicit-dependencies)
        - 明示的にリソースの依存関係を指定したい場合に使う
            - めったに使うことはないと書かれている
            - （が、実際には結構ある・・・）
                - 例えばopenstack providerの場合以下のようなことが起きる
                    - instance作成時に、instance.network.uuid経由でnetworkを指定する
                    - networkにはsubnetというCIDRを管理するためのリソースがある
                    - networkにinstanceを接続すると、裏ではportというリソースが作られる。（接続のためのIPを保有するリソース)これがsubnetの情報を使う
                    - 仮に以下のようなtfファイル（かつdepends_onがない場合)でterraform apply後にterraform destroyをした場合、instanceがnetworkを参照しているため、instanceが消えるとnetworkの削除がすかさず開始されてしまうが、実際にはsubnetが残存していることがあるため、destroy時にエラーになることがある
    
                      ```go
                      resource "openstack_networking_network_v2" "net_1" {
                        name  = "net_1"
                      }
                      
                      resource "openstack_networking_subnet_v2" "sub_1" {
                        network_id = "${openstack_networking_network_v2.net_1.id}"
                        cidr       = "10.10.1.0/24"
                        gateway_ip = "10.10.1.1"
                      }
                      
                      resource "openstack_compute_instance_v2" "instance_1" {
                        name      = "instance_1"
                        flavor_id = "..."
                        depends_on = ["openstack_networking_subnet_v2.sub_1"]
                      
                        network {
                          uuid = "${ecl_networking_network_v2.net_1.id}"
                        }
                      }
                      ```
    
                    - そこで上のようにdepends_onしている
    - [Connection block](https://www.terraform.io/docs/configuration-0-11/resources.html#connection-block) / [Provisioners](https://www.terraform.io/docs/configuration-0-11/resources.html#provisioners)
        - （ちょっとこの辺は根本的に勉強不足なので別途とさせてください) :bow:
- [Using Variable With Count](https://www.terraform.io/docs/configuration-0-11/resources.html#using-variables-with-count)
    - （ぱっと見ここで特筆すべき点もなかった気がするので割愛）
    - （上で書かれている count.index の考え方を例にしただけのように見えるので）
- [Multiple Provider Instances](https://www.terraform.io/docs/configuration-0-11/resources.html#multiple-provider-instances)
    - （例えばOpenStackを例に取った場合）
        - providerブロックでリソース作成先のテナント、およびそれに対する認証情報を書く
        - では複数のテナントに対してリソースを同時に作りたかったらどうするのか？
        - ・・・というような問題を解決するために使う
        - 例えば以下のような書き方ができる

        ```go
        provider "openstack" {
          alias             = "jp1"
          auth_url          = "https://jp1.keystone.com/v3/"
          user_name         = "*****"
          password          = "*****"
          tenant_id         = "<https://jp1.keystone.com/v3/ のテナントID>"
          project_domain_id = "default"
        }
        
        provider "openstack" {
          alias             = "jp4"
          auth_url          = "https://jp4.keystone.com/v3/"
          user_name         = "*****"
          password          = "*****"
          tenant_id         = "<https://jp4.keystone.com/v3/ のテナントID>"
          project_domain_id = "default"
        }
        
        resource "openstack_compute_keypair_v2" "keypair_jp4" {
          name     = "keypair_1"
          provider = "openstack.jp4"
        }
        
        resource "openstack_compute_keypair_v2" "keypair_jp1" {
          name     = "keypair_1"
          provider = "openstack.jp1"
        }
        ```

## [Data Sources](https://www.terraform.io/docs/configuration-0-11/data-sources.html)

- Terraformの外、もしくは異なるコンフィグレーションにより生成されたリソースを取得するための機能
    - リソースは作成および以降のリソースマネジメントを司る
    - データソースは読み込み専用ではあるが、既存データに対する参照のみを司る
- 定義の仕方は以下の通り
    - 基本的にはリソースとほぼ同じだが、 `data` でブロックを開始する

    ```go
    data "aws_ami" "web" {
      filter {
        name   = "state"
        values = ["available"]
      }

      filter {
        name   = "tag:Component"
        values = ["web"]
      }

      most_recent = true
    }
    ```
    - で、それをリソース等から [Interpolation Syntax](https://www.terraform.io/docs/configuration-0-11/interpolation.html)経由で読み出す

    ```go
    resource "aws_instance" "web" {
      ami           = "${data.aws_ami.web.id}"
      instance_type = "t1.micro"
    }
    ```

    - （要はクエリをしてTerraform管轄外のリソースを引当て、それをTerraform管轄のリソースから参照したいときに使う）
        - （設計にもよるが、うちらのシステムではOpenStackのフレーバーのIDは毎回変わっちゃうけど、名前は変わらないようにしてある。なら名前で検索してIDは毎回 `.tf` 内では意識しないようにしたいよね・・・とか、そういうときに）
- [Meta-parameters](https://www.terraform.io/docs/configuration-0-11/data-sources.html#meta-parameters)
    - Resource同様メタパラメータも利用できる
        - ただし、 `lifecycle` は使えない（参照ONLYだから当然ですね）
- [Multiple Provider Instances](https://www.terraform.io/docs/configuration-0-11/data-sources.html#multiple-provider-instances)
    - これもリソースと同じ原理で利用可能

### [Providers](https://www.terraform.io/docs/configuration-0-11/providers.html)

- リソースのCRUDに対して責任を持つ
    - （プロバイダ内に定義したリソースやデータソースを `.tf` から使えるようになるよというのがTerraformの原理なので）
- 認証情報とか、エンドポイントのURLとか、そういうものを provider ブロックの中に定義する
- 通常リソースの名前の冒頭とプロバイダ名を合致させる
- 定義の仕方は以下の通り
    - (シンプルに) `provider "プロバイダ名" { ... }` と記載する
    - 以下の引数はTerraform自体によってハンドルされる特別な引数。それ以外はプロバイダによってハンドルされる
        - alias
        - version
- [Initialization](https://www.terraform.io/docs/configuration-0-11/providers.html#initialization)
    - providerブロックをコンフィグに追記した場合、プロバイダの利用前に初期化処理が必要
    - この初期化処理において、可能な場合はプロバイダのバイナリがダウンロードされる
        - ただし HashiCorp から配信されていないプロバイダ(つまりOfficial Providerではないもの）については自動ではダウンロードされない
    - コマンド的には `terraform init` がこの処理に相当する
    - プロバイダがインストールされるのはあくまで現在の作業ディレクトリに対してのみ。ディレクトリが変わればDL済プロバイダも異なる
- [Provider Versions](https://www.terraform.io/docs/configuration-0-11/providers.html#provider-versions)
    - `terraform init` の際、以下のような動きとなる
        - （多分）プロバイダのバージョンが明示されない場合はおそらく最新版がインストールされる
        - （多分）明示した場合は
            - バージョンを直接(というか符号無しで)指定した場合はそのバージョンがインストールされる

            ```go
            provider "openstack" {
                version = "1.0" // 1.0がインストールされる
            }
            ```
            - それ以外の状況は、 [Provider Versions](https://www.terraform.io/docs/configuration-0-11/providers.html#provider-versions) の記載を参照
    - 一度プロバイダがインストールされると、コンフィグ内に記載したversionを変更しても、 `terraform init` だけでは再インストールはされない
        - 更新したい場合は `terraform init -upgrade` とする必要がある
            - （ダウングレードもされる模様。つまり最新化。例えばすでに openstack provider 1.19.0がインストールされた状態で、 `version = "1.0"` を明記し `terraform init -upgrade` すると、 `1.0` が上書きインストールされたので）
- [Multiple Provider Instances](https://www.terraform.io/docs/configuration-0-11/providers.html#multiple-provider-instances)
    - （すでに書かれている内容なので省略）
- [Interpolation](https://www.terraform.io/docs/configuration-0-11/providers.html#interpolation)
    - （すでに書かれている内容なので省略）
- [Third-party Plugins](https://www.terraform.io/docs/configuration-0-11/providers.html#third-party-plugins)
    - Official Providerではない場合、 `terraform init` だけではプロバイダが勝手にインストールされない
    - OS毎に以下のディレクトリに配置して置く必要がある
        - Windows: `%APPDATA%\terraform.d\plugins`
        - それ以外: `~/.terraform.d/plugins`
            - （と書かれてはいるが・・・）
                - 実際にはTerraformの作業ディレクトリ配下の
                    - `<work_directory>/.terraform/plugins/darwin_amd64/` に配置して置くほうが正解に思える（OS毎にパスは変える必要がある）
                    - 実際 `terraform init` ではそこにインストールされるし
                    - それに作業ディレクトリが変わればプロバイダの内容は異なる・・・とドキュメント内にも明記があるわけで
    - [Plugin Names and Versions](https://www.terraform.io/docs/configuration-0-11/providers.html#plugin-names-and-versions)
        - バイナリの命名は `terraform-provider-<NAME>_vX.Y.Z` としておくと、Terraformばプロバイダのバージョンを把握できる
    - [OS and Architecture Directories](https://www.terraform.io/docs/configuration-0-11/providers.html#os-and-architecture-directories)
        - プラグインディレクトリの名前の付け方について
        - `darwin_amd64` とか `windows_amd64` とかについて記載あり
- [Provider Plugin Cache](https://www.terraform.io/docs/configuration-0-11/providers.html#provider-plugin-cache)
    - `terraform init` は作業ディレクトリに個別にプラグインのバイナリをダウンロードする
    - つまり複数の作業ディレクトリがあると、同一のバイナリが複数箇所に存在することになる
    - なので、 [CLI configuration file](https://www.terraform.io/docs/commands/cli-config.html) に `plugin_cache_dir` を記載することでその集約が可能
        - これは `TF_PLUGIN_CACHE_DIR` という環境変数でも代替可能である

### [Variables](https://www.terraform.io/docs/configuration-0-11/variables.html)

- 値を定義し、コンフィグの各所から参照させるための仕組み
- CLIの引数や環境変数経由で与えることができる
    - CLIにおける `terraform apply -var <変数名>=<変数値>` もしくは `export TF_VAR_<変数名>="<変数値>"` として付与可能
- 定義の仕方は以下の通り
    - `variable` ブロックに定義
    - ブロック内に指定可能なキーは以下
        - type (Optional)
            - `string` , `list` , `map` の指定が可能
            - typeを省略すると、 `default` で指定した値に応じてtypeが決まる
            - それも省略されている場合は `string` とみなされる
        - default (Optional)
            - 変数の指定がない場合のデフォルト値を指定する
            - もしこの記載がなく、かつ外部から与えられない場合はTerraformがエラーを返す
            - ここに interpolation expression(補間)は使ってはならない
        - description (Optional)
            - 変数に対する説明を指定する
            - [Terraform Registry](https://registry.terraform.io/) のドキュメントにも表示される
        - Booleanは現在(0.11では)利用できない
- [Environment Variables](https://www.terraform.io/docs/configuration-0-11/variables.html#environment-variables)
    - `TF_VAR_xxxx` として、環境変数経由で変数を付与することができる
    - リストとやマップも同方式にて付与可能
- [Variable Files](https://www.terraform.io/docs/configuration-0-11/variables.html#variable-files)
    - `terraform apply` 時の `-var-file=FILE` で与えることができる
    - また以下の名前のファイルは自動的に変数定義ファイルとして読み込まれる
        - `terraform.tfvars`
        - `*.auto.tfvars`
    - （あくまで変数値を与えるためのファイルであり、 `variable` ブロックを定義するためのものではない）
        - 要は `-vars` とか `export TF_VARS_...` の代替
- [Variable Merging](https://www.terraform.io/docs/configuration-0-11/variables.html#variable-merging)
    - 同一名称の変数に対し、値の付与が複数回出てきた場合、最後の値が利用される
- [Variable Precedence](https://www.terraform.io/docs/configuration-0-11/variables.html#variable-precedence)
    - `-vars` 同様、 `-var-file` も同じように後読みしたものが適用される


### [Outputs](https://www.terraform.io/docs/configuration-0-11/outputs.html)

- `terraform apply` 後に強調出力したかったり、 `terraform output` で参照を容易化したいような内容を `output` ブロック配下に書く
- 定義の仕方は以下の通り
    - `output "<NAME>" { ... }`
    - `NAME` は `英数`, `-` , `_` で定義
    - ブロック内に定義可能なキーは以下の通り
        - value (Required)
            - 出力したい値そのもの
        - description (Optional)
            - 説明用文字列
            - そのうち `terraform` コマンド内で表示されるようになるかも
        - depends_on (list of strings)
            - （あるリソースの作成後にoutputを生成したいというような）明確な依存関係を定義したい場合に記載
        - sensitive (Optional, boolean)
            - これをtrueにすると、 `terraform apply` , `terraform refresh` 時には `<sensitive>` とだけ表示されるようになる
            - `terraform output` 時は問題なく表示される
            - ただstateには当然(sensitiveにしたとしても、当然生の値が保存されているのでそれを見ることで値は分かってしまう点には注意

### [Local Values](https://www.terraform.io/docs/configuration-0-11/locals.html)

これについては調べていた過程でわからないことが多すぎたので別記事に切り出した。

- [TerraformのLocal Valuesについてよくわからなかったので検証してみた](https://qiita.com/keiichi-hikita/items/09ea25fab094986f4950)


### [Module](https://www.terraform.io/docs/configuration-0-11/modules.html)

- 複数のリソースを束ねたコンテナのようなもの
- 基本的に普通の使い方をした場合の `.tf` ファイル群は `root module` と呼ばれる
- モジュールから他のモジュールを呼び出すことが出来、かつ複数回可能である
- モジュールについて特化した内容は [こちら](https://www.terraform.io/docs/modules/index.html) を参照

- [Calling a Child Module](https://www.terraform.io/docs/configuration-0-11/modules.html#calling-a-child-module)
    - モジュールの呼び出しは `module "<NAME>" {}` で行う
    - ブロック内の引数の大半は、モジュールにおいて定義されている入力値に対応する
    - それ以外のものとして以下がある
        - source
            - すべてのモジュール利用時に必須のパラメータ
            - モジュールのパスを指定する
            - interpolations syntaxは利用できない
    - モジュールを新規に作成した場合、 `terraform init` で再認識される必要がある
    - すでに認識済みのモジュールを修正した場合は `terraform init -upgrade` する必要がある
        - （とあるが、作業ディレクトリ配下のmodulesディレクトリ(./modules)とかに置いてあるモジュールは修正内容が即時反映されるので、おそらく Terraform Registryとかから取得したモジュールのことではないかと）
- [Accessing Module Output Values](https://www.terraform.io/docs/configuration-0-11/modules.html#accessing-module-output-values)
    - モジュールは通常カプセル化されてるため、属性に対して親から直接アクセスすることは出来ないが、outputを利用することでそれが可能となる
    - （実際にやってみる）
    - [こちら](https://qiita.com/keiichi-hikita/items/09ea25fab094986f4950) に近いコードイメージ説明
    - main.tf (rootモジュール側)

      ```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}"
      }
      
      output "private_key" {
        value = {
          name        = "${module.keypair.name}"
          private_key = "${module.keypair.private_key}"
        }
      }
      ```

      - modules/main.tf (module側)

      ```go
      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}"
      }
      
      output "private_key" {
        value = "${ecl_compute_keypair_v2.keypair_1_module.private_key}"
      }
      
      output "name" {
        value = "${ecl_compute_keypair_v2.keypair_1_module.name}"
      }
      ```

      - この状態で `terraform apply` すると最終的に以下の結果が出力される

      ```go
      Outputs:

      private_key = {
        name = stage-key-module
        private_key = -----BEGIN RSA PRIVATE KEY-----
      MIIEpAIBAA ... (略)
      -----END RSA PRIVATE KEY-----
      }
      ```
    - 上記のようなイメージで、 rootモジュールのoutputからmoduleのoutput経由で、module内keypairのname, private_keyを表示することが出来た
- [Module Versions](https://www.terraform.io/docs/configuration-0-11/modules.html#module-versions)
    - プロバイダと同じようにVersionの指定ができる
    - 基本的な指定方法もほぼ同じ
- [Other Meta-arguments](https://www.terraform.io/docs/configuration-0-11/modules.html#other-meta-arguments)
    - sourceの他のMeta系引数についての解説
    - version (Optional)
        - 上記記載のバージョン制限を与えたい場合に使用
    - providers (Optional)
        - 明確にモジュールに対してプロバイダ設定を与えたい場合に使う
            - 詳細は [こちら](https://www.terraform.io/docs/configuration-0-11/modules.html#passing-providers-explicitly) を参照
        - もちろん指定がなければ、エイリアスされていないプロバイダ設定（いわゆる普通の設定）をmodule側は自動的に引き継ぐ
    - それ以外に `count`, `for_each` and `lifecycle` が使えるようになるらしい(0.12でなった？)
- [Providers within Modules](https://www.terraform.io/docs/configuration-0-11/modules.html#providers-within-modules)
    - 基本的にproviderブロックはrootモジュールに置いて、下段のモジュールに引き継いでいくべき
        - プロバイダ定義をモジュール側で実施することで、rootモジュールの設定がロストするというような状態を防止するほうが望ましい
    - その場合、
        - 暗黙的に渡す方法と
        - モジュールのproviders引数を利用して明示的に渡す方法がある
    - [Implicit Provider Inheritance](https://www.terraform.io/docs/configuration-0-11/modules.html#implicit-provider-inheritance)
        - rootモジュールにだけproviderブロックを定義し、あとはそれが自動的に継承されていくやり方
        - 複雑なケースの場合は、 [multiple provider instances](https://www.terraform.io/docs/configuration/providers.html#multiple-provider-instances)を活用するか、この後の明示的な渡し方を使用する
    - [Passing Providers Explicitly](https://www.terraform.io/docs/configuration-0-11/modules.html#passing-providers-explicitly)
        - モジュールに明示的にプロバイダ設定を渡したい場合は、providers引数を使用する
        - modulesブロック内のproviders引数を使用した場合、プロバイダ設定については通常の継承方法が途切れる
        - エイリアスとして作成されたプロバイダ設定は自動で子モジュールには引き継がれないので、その場合は明示的に渡す必要がある
        - モジュール側にproviderブロックを準備しており、かつそれにもエイリアスがあるような場合、module使用側のproviderブロックでは、 `<provider>.<alias>` というキー名でproviderを明示的に渡す必要がある
            - （ここに書かれている例は、以下のようなことをやっている）
                - モジュール内では、２つのproviderブロックを定義して、それぞれにaliasとして `src` , `dst` という内容を定義している
                - これに対してrootモジュールで作成した２つのエイリアス `aws.usw1` と `aws.usw2` を、modulesブロックのprovidrs引数を使用して渡している
- [Multiple Instances of a Module](https://www.terraform.io/docs/configuration-0-11/modules.html#multiple-instances-of-a-module)
    - １つのモジュールを同一のモジュール(rootモジュールなど)から複数回呼び出すことは可能である
    - （まぁそりゃそうですよね・・・。あとはここについては特記すべき点はないかと）
- [Tainting resources within a module](https://www.terraform.io/docs/configuration-0-11/modules.html#tainting-resources-within-a-module)
    - `terraform taint` において特定のモジュール全体をtaint扱いにすることは出来ない
    - `-module`引数を利用した上で、モジュールの特定のリソースに対して行う必要がある

### [Terraform](https://www.terraform.io/docs/configuration-0-11/terraform.html)

- Terraform自体に対する設定を行うためのブロック
- 利用可能な引数は以下
    - required_version
        - [Specifying a Required Terraform Version](https://www.terraform.io/docs/configuration-0-11/terraform.html#specifying-a-required-terraform-version) のようにバージョンの指定を行うことができる
    - backend
        - [backends](https://www.terraform.io/docs/backends/index.html) 参照
- このブロック内では一切の変数補間が利用できない

### [Terraform Push Configuration](https://www.terraform.io/docs/configuration-0-11/terraform-enterprise.html)

deprecateらしいので省略

### [Environment Variables](https://www.terraform.io/docs/configuration-0-11/environment-variables.html)

- TF_LOG
    - 何かしらがセットされていると詳細なログが出る
    - セット解除もしくはemptyにセットし直すとそれが止まる
- TF_LOG_PATH
    - ログを出したい場合にその先を指定
    - TF_LOGと組み合わせて使う
- TF_INPUT
    - この値を `0` もしくは `false` に設定することで `-input=false` と同等の効果
- TF_MODULE_DEPTH
    - `-module-depth=N` と同等
    - `terraform plan` , `terraform graph` の表示深度に影響する（っぽい）
- TF_VAR_name
    - この環境変数経由で、 `-var` と同等に変数を付与可能
- TF_CLI_ARGS
    - `terraform` コマンドの引数を強制的に付与可能
    - 実際に付与された引数とマージされる
- TF_CLI_ARGS_name
    - `terraform` コマンドの特定のケースにおける引数を矯正付与
    - 例えば、 `terraform plan` のみに矯正付与したい場合は `TF_CLI_ARGS_plan` を設定すればよい
- TF_DATA_DIR
    - 通常カレントディレクトリ配下の `.terraform` に書かれる内容を、この環境変数により別ディレクトリに移すことができる
- TF_SKIP_REMOTE_TESTS
    - この環境変数をセットすると、unittestを行う際、リモート接続が必要となるものを強制スキップする


以上です。

長くなりましたが自分のバイブルとして保存しておきます。

で、次に0.12の方を読んで差分を洗い出していきたいと思いますがそれは別記事にて。