我试用了 Terraform 0.13 版的新功能

首先

terraform的0.13版本在8月10日发布。
由于我经常使用terraform,我对这个更新添加了哪些功能非常感兴趣,所以我将进行验证。考虑到我最近正在使用Azure,我会使用Azure进行验证。

2. 通过升级版本添加的新功能

新增的功能有以下五个。

    1. 在module块内可以使用count和for_each,并且可以创建多个资源

 

    1. https://www.terraform.io/docs/configuration/modules.html#multiple-instances-of-a-module

在module块内可以使用depends_on,并且可以定义模块的依赖关系
https://www.terraform.io/docs/configuration/modules.html#other-meta-arguments

在variable块内可以进行验证
https://www.terraform.io/docs/configuration/variables.html#custom-validation-rules

可以自动安装第三方提供商
https://www.terraform.io/docs/configuration/provider-requirements.html

可以将状态文件保存到kubernetes中
https://www.terraform.io/docs/backends/types/kubernetes.html

由于1~3可能会在将来被使用,所以我会总结试验的结果。

3, 实践

3.1. Terraform 更新

为了使用新功能,首先需要更新terraform。
我使用Homebrew进行安装,所以我使用以下命令来更新terraform。

$ brew upgrade terraform

接下来可以参考官方网站的”升级到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”代码块。根据变更,现在需要在此代码块中进行提供程序的指定。

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的模块中,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文件夹下的源代码如下:

■ 模块/资源组/main.tf

resource azurerm_resource_group common_rg {
  name     = var.name
  location = var.location

  tags = {
    environment = var.environment
  }
}

■ 模块/资源组/变量.tf

variable name {
  description = "リソースグループ名"
}

variable location {
  description = "リージョン名"
}

variable environment {
  description = "環境名"
}

根据以上来源,创建资源组。总结起来,使用 count 和 for_each 创建资源的差异在于编写方式稍有不同,模块名称也存在以下差异。

    • count : module.resource_group[0]…

 

    for_each : module.resource_group[“terraform013-test-1”]…

让我们在查看实际源代码和Terraform执行结果的同时,确认一下达到这个结论的整个过程。

我会用不同的模式编写develop中的源代码。

3.2.1的for_each模式

■ 开发/主要.tf

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

  for_each    = toset(var.name)
  name        = each.key
  location    = var.location 
  environment = var.environment
}

■ 开发/变量.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. 计数模式

■ 开发/主.tf

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

  count       = length(var.name)
  name        = element(var.name, count.index)
  location    = var.location 
  environment = var.environment
}

■ 开发/变量.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 來創建兩個資源組。
如果我們觀察兩個差異,可以看到寫法有些差異,且模組名也有以下差異。

    • count : module.resource_group[0]…

 

    for_each : module.resource_group[“terraform013-test-1”]…

考虑到可读性,对于每个元素的操作,我认为使用for_each更容易理解该资源的用途,因此个人倾向于积极使用for_each。

3.3. module内的depends_on

接下来,我们将验证在模块块中的depends_on功能。
通常情况下,terraform会自动解决依赖关系,但如果存在无法看到的资源之间的依赖关系,就需要有意地更改创建顺序。这个功能非常方便用于解决依赖关系。

这次我们将作为验证来看一下通过添加depends_on的方式,行为的差异。
我们设定了一个场景,与3.2版本类似,创建两个Azure资源组。
接下来我们将编写各自的源码。

3.3.1 无需依赖

这段源代码请按照以下方式编写并执行。

■ 开发/主要.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
}

■ 变量.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 依赖于 あり

这段代码如下所示。

■ 开发/主要.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 ]
}

■ 变量配置文件/开发.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. 验证变量内的内容

使用这个功能,现在可以对定义的变量进行验证检查。
我将尝试使用这个功能。

本次假设情景为,当环境名称不在dev、stg、prod之外时,将会触发验证检查错误。接下来我将编写一段代码来模拟这种情况。

■开发/变量.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 版本更新之后的三个新功能。
这三个功能都是我们期待已久的,所以我们将会在将来充分利用它们。
特别是验证功能,它可以便捷地通知团队成员统一命名规则和云环境的限制,所以我打算大量使用它。

另外,我也注意到 Kubernetes 现在可以存储状态文件,所以我打算在正式引入 Kubernetes 时使用它。

广告
将在 10 秒后关闭
bannerAds