CLOVER🍀

That was when it all began.

Terraformのprovidersコマンドを試す

これは、なにをしたくて書いたもの?

Terraformのprovidersというコマンドにちょっと興味があったので、調べてみようかと。

terraform providersコマンド

providersと言っているのは、terraformに含まれるコマンドです。

Command: providers - Terraform by HashiCorp

providersコマンドを使うと、現在の構成ファイルで使用しているTerraform Providerの情報を、依存関係を解析したうえで
表示してくれます。

さらにサブコマンドもあり、mirrorとschemaがあります。

Command: providers mirror - Terraform by HashiCorp

Command: providers schema - Terraform by HashiCorp

mirrorは、現在の構成に必要なProviderをダウンロードし、オフラインでも使えるようにする機能です。Terraformを利用する際に、
Providerを外部からダウンロードできない場合などに使用するようです。

こちらは、今回はパス。

schemaは、Providerが持っているリソースやデータソースに関するスキーマ情報を出力してくれます。こちらは、今回試して
みようと思います。

環境

今回の環境は、こちら。

$ terraform version
Terraform v0.13.3

この状態で、試していきます。

providersコマンドを試す

まずは、providersコマンドを試してみます。

Command: providers - Terraform by HashiCorp

こんな構成ファイルを用意。今回はConsul Providerを使用しました。
main.tf

terraform {
  required_version = "0.13.3"

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = "2.10.0"
    }
  }
}

provider "consul" {
}

terraform initして

$ terraform init

確認すると、こんな感じに表示されます。

$ terraform providers

Providers required by configuration:
.
└── provider[registry.terraform.io/hashicorp/consul] 2.10.0

ソースコードを見ると、構成(Configuration)の他に、Stateに関するProviderも表示してくれるようです。

https://github.com/hashicorp/terraform/blob/v0.13.3/command/providers.go#L117-L125

もう少しバリエーションを試してみます。

2つProviderを使っている場合。Consul ProviderとMySQL Providerを使用。
main.tf

terraform {
  required_version = "0.13.3"

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = "2.10.0"
    }

    mysql = {
      source  = "terraform-providers/mysql"
      version = "1.9.0"
    }
  }
}

provider "consul" {
}

provider "mysql" {
}

terraform init後に実行すると、こんな感じになります。

$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/consul] 2.10.0
└── provider[registry.terraform.io/terraform-providers/mysql] 1.9.0

この表示内容は、versionの書き方に依存するようなので、たとえばこんな感じに変更すると

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = ">= 2.10"
    }

    mysql = {
      source  = "terraform-providers/mysql"
      version = ">= 1.9"
    }
  }

terraform providersの結果にも反映されます。

$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/consul] >= 2.10.*
└── provider[registry.terraform.io/terraform-providers/mysql] >= 1.9.*

最後に、backendも付けてみます。backendにはConsulを使用しました。
main.tf

terraform {
  required_version = "0.13.3"

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = "2.10.0"
    }

    mysql = {
      source  = "terraform-providers/mysql"
      version = "1.9.0"
    }
  }

  backend "consul" {
    address = "172.17.0.2:8500"
    scheme  = "http"
    path    = "state"
    lock    = true
  }
}

provider "consul" {
}

provider "mysql" {
  endpoint = "172.17.0.3:3306"
  username = "root"
  password = "password"
}

resource "mysql_database" "app" {
  name = "my_database"
}

ちょっと、リソース定義自体が増えていますが…。

まずはinitします。

$ terraform init

この時点では、Stateが使用しているProviderは実は現れません。

$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/terraform-providers/mysql] 1.9.0
└── provider[registry.terraform.io/hashicorp/consul] 2.10.0

では、applyしてみます。

$ terraform apply

すると、State側のProviderも出現します。

$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/terraform-providers/mysql] 1.9.0
└── provider[registry.terraform.io/hashicorp/consul] 2.10.0

Providers required by state:

    provider[registry.terraform.io/terraform-providers/mysql]

ですが、出現したのはMySQL Providerですね。

これって、もしかして「Stateに情報を保存しているProvider」の情報を表示しているんでしょうか?

そこで、構成ファイルを変更して、Consulに関するリソースも追加してみます。
main.tf

terraform {
  required_version = "0.13.3"

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = "2.10.0"
    }

    mysql = {
      source  = "terraform-providers/mysql"
      version = "1.9.0"
    }
  }

  backend "consul" {
    address = "172.17.0.2:8500"
    scheme  = "http"
    path    = "state"
    lock    = true
  }
}

provider "consul" {
  address = "172.17.0.2:8500"
  scheme  = "http"
}

provider "mysql" {
  endpoint = "172.17.0.3:3306"
  username = "root"
  password = "password"
}

resource "consul_config_entry" "web" {
  name = "web"
  kind = "service-defaults"

  config_json = jsonencode({
    Protocol = "http"
  })
}

resource "mysql_database" "app" {
  name = "my_database"
}

terraform apply後に確認すると、このようになります。

$ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/terraform-providers/mysql] 1.9.0
└── provider[registry.terraform.io/hashicorp/consul] 2.10.0

Providers required by state:

    provider[registry.terraform.io/hashicorp/consul]

    provider[registry.terraform.io/terraform-providers/mysql]

やっぱり、Stateに情報を保存しているProviderが表示されているようですね。

そもそも、State自体のソースコードはTerraform本体に含まれているようなので、backendに選んだものが現れるわけではない、と。…。

https://github.com/hashicorp/terraform/tree/v0.13.3/backend/remote-state

このあたり、そのうちもう少しだけ追いたいところ。

providers schemaコマンド

次は、schemaコマンド。schemaコマンドを使用することで、構成ファイルで使用しているProviderのスキーマ情報を
表示してくれます。

Command: providers schema - Terraform by HashiCorp

たとえば、Consul Providerを使用するこんな構成ファイルがあったとして main.tf

terraform {
  required_version = "0.13.3"

  required_providers {
    consul = {
      source  = "hashicorp/consul"
      version = "2.10.0"
    }
  }
}

provider "consul" {
}

initします。

$ terraform init

その後、providers schemaコマンドを実行します。-jsonオプションが必要です。

$ terraform providers schema -json | jq

結果は、jqで整形しています。

{
  "format_version": "0.1",
  "provider_schemas": {
    "registry.terraform.io/hashicorp/consul": {
      "provider": {
        "version": 0,
        "block": {
          "attributes": {
            "address": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "ca_file": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "ca_path": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "ca_pem": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },

〜省略〜

            "token": {
              "type": "string",
              "description_kind": "plain",
              "optional": true,
              "sensitive": true
            }
          },
          "description_kind": "plain"
        }
      },
      "resource_schemas": {
        "consul_acl_auth_method": {
          "version": 0,
          "block": {
            "attributes": {
              "config": {
                "type": [
                  "map",
                  "string"
                ],
                "description": "The raw configuration for this ACL auth method.",
                "description_kind": "plain",
                "optional": true
              },
              "config_json": {
                "type": "string",
                "description": "The raw configuration for this ACL auth method.",
                "description_kind": "plain",
                "optional": true
              },
              "description": {
                "type": "string",
                "description": "A free form human readable description of the auth method.",
                "description_kind": "plain",
                "optional": true
              },
              "display_name": {
                "type": "string",
                "description": "An optional name to use instead of the name attribute when displaying information about this auth method.",
                "description_kind": "plain",
                "optional": true
              },
              "id": {
                "type": "string",
                "description_kind": "plain",
                "optional": true,
                "computed": true
              },

〜省略〜

      },
      "data_source_schemas": {
        "consul_acl_auth_method": {
          "version": 0,
          "block": {
            "attributes": {
              "config": {
                "type": [
                  "map",
                  "string"
                ],
                "description_kind": "plain",
                "computed": true
              },
              "config_json": {
                "type": "string",
                "description": "The raw configuration for this ACL auth method.",
                "description_kind": "plain",
                "computed": true
              },
              "description": {
                "type": "string",
                "description_kind": "plain",
                "computed": true
              },
              "display_name": {
                "type": "string",
                "description_kind": "plain",
                "computed": true
              },

〜省略〜

            },
            "description_kind": "plain"
          }
        }
      }
    }
  }
}

Provider自体のスキーマ情報、リソース、そしてデータソースのスキーマ情報を表示してくれます。

ブロックで定義する属性の情報は、こんな感じ。

            "block_types": {
              "namespace_rule": {
                "nesting_mode": "list",
                "block": {
                  "attributes": {
                    "bind_namespace": {
                      "type": "string",
                      "description_kind": "plain",
                      "required": true
                    },
                    "selector": {
                      "type": "string",
                      "description_kind": "plain",
                      "optional": true
                    }
                  },
                  "description_kind": "plain"
                }
              }
            },

ドキュメントにも記載があります。

Block Representation

また、ドキュメントではわからない、各属性のtypeなどがわかって便利なのですが、さすがにデフォルト値や
map等の属性に関してはわからないですね…。

              "config": {
                "type": [
                  "map",
                  "string"
                ],
                "description_kind": "plain",
                "required": true
              },

このあたりは、ドキュメントを頼るしかないでしょう。

このproviders schemaコマンドは、使用しているリソース等のスキーマ定義ではなく、依存しているProviderのスキーマ定義を
表示するので、使っていないリソース等のスキーマも表示してくれます。

ゆえに、大量に表示されるのですが…。

必要なものに絞って出力してもよいでしょう。

たとえば、consul_serviceリソースだけをjqコマンドで抜粋してみます。

consul_service

$ terraform providers schema -json | jq '.provider_schemas."registry.terraform.io/hashicorp/consul".resource_schemas.consul_service'
{
  "version": 0,
  "block": {
    "attributes": {
      "address": {
        "type": "string",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "datacenter": {
        "type": "string",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "enable_tag_override": {
        "type": "bool",
        "description_kind": "plain",
        "optional": true
      },
      "external": {
        "type": "bool",
        "description_kind": "plain",
        "optional": true
      },
      "id": {
        "type": "string",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "meta": {
        "type": [
          "map",
          "string"
        ],
        "description_kind": "plain",
        "optional": true
      },
      "name": {
        "type": "string",
        "description_kind": "plain",
        "required": true
      },
      "namespace": {
        "type": "string",
        "description_kind": "plain",
        "optional": true
      },
      "node": {
        "type": "string",
        "description_kind": "plain",
        "required": true
      },
      "port": {
        "type": "number",
        "description_kind": "plain",
        "optional": true
      },
      "service_id": {
        "type": "string",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "tags": {
        "type": [
          "list",
          "string"
        ],
        "description_kind": "plain",
        "optional": true
      }
    },
    "block_types": {
      "check": {
        "nesting_mode": "set",
        "block": {
          "attributes": {
            "check_id": {
              "type": "string",
              "description_kind": "plain",
              "required": true
            },
            "deregister_critical_service_after": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "http": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "interval": {
              "type": "string",
              "description_kind": "plain",
              "required": true
            },
            "method": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "name": {
              "type": "string",
              "description_kind": "plain",
              "required": true
            },
            "notes": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "status": {
              "type": "string",
              "description_kind": "plain",
              "optional": true,
              "computed": true
            },
            "tcp": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "timeout": {
              "type": "string",
              "description_kind": "plain",
              "required": true
            },
            "tls_skip_verify": {
              "type": "bool",
              "description_kind": "plain",
              "optional": true
            }
          },
          "block_types": {
            "header": {
              "nesting_mode": "set",
              "block": {
                "attributes": {
                  "name": {
                    "type": "string",
                    "description_kind": "plain",
                    "required": true
                  },
                  "value": {
                    "type": [
                      "list",
                      "string"
                    ],
                    "description_kind": "plain",
                    "required": true
                  }
                },
                "description_kind": "plain"
              }
            }
          },
          "description_kind": "plain"
        }
      }
    },
    "description_kind": "plain"
  }
}

スキーマに関する情報は、このあたりを参考に。

https://github.com/hashicorp/terraform/blob/v0.13.3/command/providers_schema.go

https://github.com/hashicorp/terraform/blob/v0.13.3/terraform/schemas.go

https://github.com/hashicorp/terraform/blob/v0.13.3/configs/configschema/schema.go