1. はじめに
terraformのバージョン0.13が8/10にリリースされました。
普段terraformを使用しているため、今回のアップデートによりどんな機能が追加になったのか個人的に気になるところを検証してみます。
私が最近Azureを使っていることから、Azureを使用して検証します。
2. バージョンアップによって追加された新機能
新しく追加された機能は以下の5つでした。
-
module
ブロック内でcount
やfor_each
が使用可能になり、複数リソースを作成可能
https://www.terraform.io/docs/configuration/modules.html#multiple-instances-of-a-module -
module
ブロック内でdepends_on
が使用可能になり、moduleの依存関係を定義可能
https://www.terraform.io/docs/configuration/modules.html#other-meta-arguments -
variable
ブロック内でvalidation
が可能に
https://www.terraform.io/docs/configuration/variables.html#custom-validation-rules - サードパーティプロバイダーの自動インストールが可能に
https://www.terraform.io/docs/configuration/provider-requirements.html - stateファイルの保存先をkubernetesに変更可能に
https://www.terraform.io/docs/backends/types/kubernetes.html
1~3は今後使うことになりそうなので、試してみた結果をまとめていきます。
3, 実践
3.1. terraform アップデート
新機能を使っていくために、まずはterraformのアップデートをします。
私はhomebrewを使ってインストールしたので、以下コマンドにてterraformをアップデートしました。
$ brew upgrade terraform
次に公式サイトのUpgrading to Terraform v0.13を参考に、terraform 0.13upgrade
コマンドを実行してみます。
$ terraform 0.13upgrade
This command will update the configuration files in the given directory to use
the new provider source features from Terraform v0.13. It will also highlight
any providers for which the source cannot be detected, and advise how to
proceed.
We recommend using this command in a clean version control work tree, so that
you can easily see the proposed changes as a diff against the latest commit.
If you have uncommited changes already present, we recommend aborting this
command and dealing with them before running this command again.
Would you like to upgrade the module in the current directory?
Only 'yes' will be accepted to confirm.
Enter a value: yes
-----------------------------------------------------------------------------
Upgrade complete!
Use your version control system to review the proposed changes, make any
necessary adjustments, and then commit.
こちらを実行するとversions.tf
というファイルが実行したパス上にできていました。
中身を見るとrequired_providers
ブロックが追加されていました。providerの指定をこのブロックで行うよう、変更になったようです。
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
}
required_version = ">= 0.13"
}
Azureプロバイダーは、v2.0.0以上からfeature
ブロックが必須となっているため、以下のように追記しておきます。
terraform {
required_providers {
azurerm = {
version = ">=2.0.0"
source = "hashicorp/azurerm"
}
}
required_version = ">= 0.13"
}
# 以下ブロックを追加してます
provider "azurerm" {
features {}
}
3.2. module内のcountとfor_each
module内でcountとfor_eachを使って、リソースの複数作成を検証してみます。
Azureのリソースグループを複数作成するシナリオを想定して、ソースコードを書きます。
フォルダ構成は以下のようにしています。
terraform_test
├── development
│ ├── main.tf
│ ├── variable.tf
│ └── versions.tf
└── modules
└── resource_group
├── main.tf
├── output.tf
└── variable.tf
development配下のmain.tfから、modules配下のソースを参照してAzureのリソースを作成します。
modules配下のソースは以下のようになっています。
■ modules/resource_group/main.tf
resource azurerm_resource_group common_rg {
name = var.name
location = var.location
tags = {
environment = var.environment
}
}
■ modules/resource_group/variable.tf
variable name {
description = "リソースグループ名"
}
variable location {
description = "リージョン名"
}
variable environment {
description = "環境名"
}
上記のソースを参照してリソースグループを作成していきます。
結論から言うと、countとfor_each にてリソースを作成した際の差分は、書き方に多少差があることと、module名に以下のような差分が出ています。
- count : module.resource_group[0]...
- for_each : module.resource_group["terraform013-test-1"]...
それでは、この結論に至るまでのプロセスを実際のソースとterraform実行結果を見ながら確認していきます。
develop配下のソースをそれぞれのパターンで書いていきます。
3.2.1. for_eachパターン
■ development/main.tf
module resource_group {
source = "../modules/resource_group"
for_each = toset(var.name)
name = each.key
location = var.location
environment = var.environment
}
■ development/variable.tf
variable name {
type = list
default = [
"terraform013-test-1",
"terraform013-test-2"
]
description = "description"
}
variable location {
type = string
default = "Japan East"
description = "リージョン名"
}
variable environment {
type = string
default = "dev"
description = "環境"
}
■ 実行結果
$ 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:
# module.resource_group["terraform013-test-1"].azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-1"
+ tags = {
+ "environment" = "dev"
}
}
# module.resource_group["terraform013-test-2"].azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-2"
+ tags = {
+ "environment" = "dev"
}
}
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: yes
module.resource_group["terraform013-test-2"].azurerm_resource_group.common_rg: Creating...
module.resource_group["terraform013-test-1"].azurerm_resource_group.common_rg: Creating...
module.resource_group["terraform013-test-1"].azurerm_resource_group.common_rg: Creation complete after 3s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-1]
module.resource_group["terraform013-test-2"].azurerm_resource_group.common_rg: Creation complete after 3s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-2]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
3.2.2. countパターン
■ development/main.tf
module resource_group {
source = "../modules/resource_group"
count = length(var.name)
name = element(var.name, count.index)
location = var.location
environment = var.environment
}
■ development/variable.tf
variable name {
type = list
default = [
"terraform013-test-1",
"terraform013-test-2"
]
description = "description"
}
variable location {
type = string
default = "Japan East"
description = "リージョン名"
}
variable environment {
type = string
default = "dev"
description = "環境"
}
■実行結果
$ 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:
# module.resource_group[0].azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-1"
+ tags = {
+ "environment" = "dev"
}
}
# module.resource_group[1].azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-2"
+ tags = {
+ "environment" = "dev"
}
}
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: yes
module.resource_group[1].azurerm_resource_group.common_rg: Creating...
module.resource_group[0].azurerm_resource_group.common_rg: Creating...
module.resource_group[0].azurerm_resource_group.common_rg: Creation complete after 3s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-1]
module.resource_group[1].azurerm_resource_group.common_rg: Creation complete after 3s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-2]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
上記のように、countとfor_eachで2つのリソースグループが作成できました。
2つの差分を見てみると、書き方に多少差があることと、module名に以下のような差分が出ています。
- count : module.resource_group[0]...
- for_each : module.resource_group["terraform013-test-1"]...
可読性を考えるとfor_eachの方が何のためのリソースなのかがわかりやすいため、個人的にはfor_eachを積極的に使っていこうと思います。
3.3. module内のdepends_on
次にmodule
ブロック内でのdepends_on
を検証してみます。
通常依存関係はterraformが解消してくれますが、依存関係が見えないリソースがあった場合、意図的に作成順序を変更する必要がありました。依存関係を解消するために、この機能は便利です。
今回は検証として、depends_onを記載することでの動作の違いを見ていきます。
3.2と同様にAzureのリソースグループを2つ作成するシナリオを想定します。
それぞれのソースを書いていきます。
3.3.1 depends_on なし
こちらのソースコードは、以下のように書き、実行します。
■ development/main.tf
module first_resource_group {
source = "../modules/resource_group"
name = var.resource_group_name.first
location = var.location
environment = var.environment
}
module secound_resource_group {
source = "../modules/resource_group"
name = var.resource_group_name.secound
location = var.location
environment = var.environment
}
■ development/variable.tf
variable resource_group_name {
type = map(string)
description = "リソースグループ名"
default = {
first = "terraform013-test-1"
secound = "terraform013-test-2"
}
}
variable location {
type = string
default = "Japan East"
description = "リージョン名"
}
variable environment {
type = string
default = "dev"
description = "環境"
}
■実行結果
$ 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:
# module.first_resource_group.azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-1"
+ tags = {
+ "environment" = "dev"
}
}
# module.secound_resource_group.azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-2"
+ tags = {
+ "environment" = "dev"
}
}
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: yes
module.secound_resource_group.azurerm_resource_group.common_rg: Creating...
module.first_resource_group.azurerm_resource_group.common_rg: Creating...
module.first_resource_group.azurerm_resource_group.common_rg: Creation complete after 1s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-1]
module.secound_resource_group.azurerm_resource_group.common_rg: Creation complete after 1s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-2]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
どちらのリソースグループも並列に作成されました。
次に depends_on を追加した際の動作を確認します。
3.3.2 depends_on あり
こちらのソースコードは、以下のようになってます。
■ development/main.tf
module first_resource_group {
source = "../modules/resource_group"
name = var.resource_group_name.first
location = var.location
environment = var.environment
}
module secound_resource_group {
source = "../modules/resource_group"
name = var.resource_group_name.secound
location = var.location
environment = var.environment
depends_on = [ module.first_resource_group ]
}
■ development/variable.tf
3.3.1と同様のため割愛
■実行結果
$ 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:
# module.first_resource_group.azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-1"
+ tags = {
+ "environment" = "dev"
}
}
# module.secound_resource_group.azurerm_resource_group.common_rg will be created
+ resource "azurerm_resource_group" "common_rg" {
+ id = (known after apply)
+ location = "japaneast"
+ name = "terraform013-test-2"
+ tags = {
+ "environment" = "dev"
}
}
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: yes
module.first_resource_group.azurerm_resource_group.common_rg: Creating...
module.first_resource_group.azurerm_resource_group.common_rg: Creation complete after 1s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-1]
module.secound_resource_group.azurerm_resource_group.common_rg: Creating...
module.secound_resource_group.azurerm_resource_group.common_rg: Creation complete after 0s [id=/subscriptions/f9d36b58-96cd-48d1-9dc8-f760b1cf174e/resourceGroups/terraform013-test-2]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
実行結果を見ると、first_resource_group
が作成された後にsecound_resource_group
が作成されました。
3.4. variable内のvalidation
こちらの機能を使うと、定義している変数をバリデーションチェックできるようになりました。
こちらの機能を試してみます。
今回は、環境名に dev
,stg
,prod
以外を指定した場合、バリデーションチェックエラーとなるようなシナリオを想定し、コードを書いてみます。
■development/variable.tf
variable environment {
type = string
default = "dev"
description = "環境"
validation {
condition = var.environment == "dev" || var.environment == "stg" || var.environment == "prod"
error_message = "The environment must be dev, stg, or prod."
}
}
■実行結果
$ terraform plan -var 'environment=test'
Error: Invalid value for variable
on variable.tf line 16:
16: variable environment {
The environment must be dev, stg, or prod.
This was checked by the validation rule at variable.tf:21,3-13.
実行結果からわかるように、バリデーションチェックができ、自分の指定したエラーメッセージを出力することができました。
チームで決まっている、ネーミングルールを統一する際や、クラウドの文字数制限等を事前にチェックできる便利な機能です。
4. まとめ
terraform 0.13のバージョンアップ後の新機能を3つ検証しました。
3つとも待ち望んでいたような機能だったので、今後有効活用していきます。
特にvalidationは、ネーミングルールの統一だったり、クラウドの制約だったりを簡単にチームメンバーにお知らせできるようになるので、どんどん使っていこうと思います。
また、kubernetesにもstateファイルを保持できるようになっているので、kubernetesを本格導入する際に使っていこうと思いました。