1.4.3.1. 输入变量

在前面的例子中,我们在代码中都是使用字面量硬编码的,如果我们想要在创建、修改基础设施时动态传入一些值呢?比如说在代码中定义Provider时用变量替代硬编码的访问密钥,或是由创建基础设施的用户来决定创建什么样尺寸的主机?我们需要的是输入变量。

如果我们把一组Terraform代码想像成一个函数,那么输入变量就是函数的入参。输入变量用variable块进行定义:

variable "image_id" {
  type = string
}

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-west-1a"]
}

variable "docker_ports" {
  type = list(object({
    internal = number
    external = number
    protocol = string
  }))
  default = [
    {
      internal = 8300
      external = 8300
      protocol = "tcp"
    }
  ]
}

这些都是合法的输入参数定义。紧跟variable关键字的就是变量名。在一个Terraform模块(同一个文件夹中的所有Terraform代码文件,不包含子文件夹)中变量名必须是唯一的。我们在代码中可以通过var.<NAME>的方式引用变量的值。有一组关键字不可以被用作输入变量的名字:

  • source
  • version
  • providers
  • count
  • for_each
  • lifecycle
  • depends_on
  • locals

输入变量只能在声明该变量的目录下的代码中使用。

输入变量块中可以定义一些属性。

1.4.3.1.1. 类型

可以在输入变量块中通过type定义类型,例如:

variable "name" {
    type = string
}
variable "ports" {
    type = list(number)
}

定义了类型的输入变量只能被赋予符合类型约束的值。

1.4.3.1.2. 默认值

默认值定义了当Terraform无法获得一个输入变量得到值的时候会使用的默认值。例如:

variable "name" {
    type    = string
    default = "John Doe"
}

当Terraform无法通过其他途径获得name的值时,var.name的值为"John Doe"

1.4.3.1.3. 描述

可以在输入变量中定义一个描述,简单地向调用者描述该变量的意义和用法:

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
}

如果在执行terraform plan或是terraform apply时Terraform不知道某个输入变量的值,Terraform会在命令行界面上提示我们为输入变量设置一个值。例如上面的输入变量代码,执行terraform apply时:

$ terraform apply
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value:

为了使的代码的使用者能够准确理解输入变量的意义和用法,我们应该站在使用者而非代码维护者的角度编写输入变量的描述。描述并不是注释!

1.4.3.1.4. 断言

输入变量的断言是Terraform 0.13.0开始引入的新功能,在过去,Terraform只能用类型约束确保输入参数的类型是正确的,曾经有不少人试图通过奇技淫巧来实现更加复杂的变量校验断言。如今Terraform终于正式添加了相关的功能。

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."

  validation {
    condition     = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

condition参数是一个bool类型的参数,我们可以用一个表达式来定义如何界定输入变量是合法的。当contidion为true时输入变量合法,反之不合法。condition表达式中只能通过var.\引用当前定义的变量,并且它的计算不能产生错误。

假如表达式的计算产生一个错误是输入变量验证的一种判定手段,那么可以使用can函数来判定表达式的执行是否抛错。例如:

variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."

  validation {
    # regex(...) fails if it cannot find a match
    condition     = can(regex("^ami-", var.image_id))
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

上述例子中,如果输入的image_id不符合正则表达式的要求,那么regex函数调用会抛出一个错误,这个错误会被can函数捕获,输出false

condition表达式如果为false,Terraform会返回error_message中定义的错误信息。error_message应该完整描述输入变量校验失败的原因,以及输入变量的合法约束条件。

1.4.3.1.5. 在命令行输出中隐藏值

该功能于 Terraofrm v0.14.0 开始引入。

将变量设置为 sensitive 可以防止我们在配置文件中使用变量时 Terraform 在 planapply 命令的输出中展示与变量相关的值。

Terraform 仍然会将敏感数据记录在状态文件中,任何可以访问状态文件的人都可以读取到明文的敏感数据值。

声明一个变量包含敏感数据值需要将 sensitive 参数设置为 true

variable "user_information" {
  type = object({
    name    = string
    address = string
  })
  sensitive = true
}

resource "some_resource" "a" {
  name    = var.user_information.name
  address = var.user_information.address
}

任何使用了敏感变量的表达式都将被视为敏感的,因此在上面的示例中,resource “some_resource” “a”的两个参数也将在计划输出中被隐藏:

Terraform will perform the following actions:

  # some_resource.a will be created
  + resource "some_resource" "a" {
      + name    = (sensitive)
      + address = (sensitive)
    }

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

在某些情况下,我们会在嵌套块中使用敏感变量,Terraform 可能会将整个块视为敏感的。这发生在那些包含有要求值是唯一的内嵌块的资源中,公开这种内嵌块的部分内容可能会暗示兄弟块的内容。

 # some_resource.a will be updated in-place
  ~ resource "some_resource" "a" {
      ~ nested_block {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }
    }

Provider 还可以将资源属性声明为敏感属性,这将导致 Terraform 将其从常规输出中隐藏。

如果打算使用敏感值作为输出值的一部分,Terraform 将要求您将输出值本身标记为敏感值,以确认确实打算将其导出。

1.4.3.1.5.1. Terraform 可能暴露敏感变量的情况

sensitive 变量是一个以配置文件为中心的概念,值被毫无混淆地发送给 Provider。如果该值被包含在错误消息中,则 Provider 报错时可能会暴露该值。例如,即使 “foo” 是敏感值,Provider 也可能返回以下错误:"Invalid value 'foo' for field"

如果将资源属性用作、或是作为 Provider 定义的资源 ID 的一部分,则 apply 将公开该值。在下面的示例中,前缀属性已设置为 sensitive 变量,但随后该值(“jae”)作为资源 ID 的一部分公开:

  # random_pet.animal will be created
  + resource "random_pet" "animal" {
      + id        = (known after apply)
      + length    = 2
      + prefix    = (sensitive)
      + separator = "-"
    }

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

...

random_pet.animal: Creating...
random_pet.animal: Creation complete after 0s [id=jae-known-mongoose]

1.4.3.1.6. 禁止输入变量为空

该功能自 Terraform v1.1.0 开始被引入

输入变量的 nullable 参数控制模块调用者是否可以将 null 分配给变量。

variable "example" {
  type     = string
  nullable = false 
}

nullable 的默认值为 true。当 nullabletrue 时,null 是变量的有效值,并且模块代码必须始终考虑变量值为 null 的可能性。将 null 作为模块输入参数传递将覆盖输入变量上定义的默认值。

nullable 设置为 false 可确保变量值在模块内永远不会为空。如果 nullablefalse 并且输入变量定义有默认值,则当模块输入参数为 null 时,Terraform 将使用默认值。

nullable 参数仅控制变量的直接值可能为 null 的情况。对于集合或对象类型的变量,例如列表或对象,调用者仍然可以在集合元素或属性中使用 null,只要集合或对象本身不为 null

1.4.3.1.7. 对输入变量赋值

1.4.3.1.7.1. 命令行参数

对输入变量赋值有几种途径,一种是在调用terraform plan或是terraform apply命令时以参数的形式传入:

$ terraform apply -var="image_id=ami-abc123"
$ terraform apply -var='image_id_list=["ami-abc123","ami-def456"]'
$ terraform plan -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}'

可以在一条命令中使用多个-var参数。

1.4.3.1.7.2. 参数文件

第二种方法是使用参数文件。参数文件的后缀名可以是.tfvars或是.tfvars.json.tfvars文件使用HCL语法,.tfvars.json使用JSON语法。

.tfvars为例,参数文件中用HCL代码对需要赋值的参数进行赋值,例如:

image_id = "ami-abc123"
availability_zone_names = [
  "us-east-1a",
  "us-west-1c",
]

后缀名为.tfvars.json的文件用一个JSON对象来对输入变量赋值,例如:

{
  "image_id": "ami-abc123",
  "availability_zone_names": ["us-west-1a", "us-west-1c"]
}

调用terraform命令时,通过-var-file参数指定要用的参数文件,例如:

terraform apply -var-file="testing.tfvars"
terraform apply -var-file="testing.tfvars.json"

有两种情况,你无需指定参数文件:

  • 当前模块内有名为terraform.tfvars或是terraform.tfvars.json的文件
  • 当前模块内有一个或多个后缀名为.auto.tfvars或是.auto.tfvars.json的文件

Terraform会自动使用这两种自动参数文件对输入参数赋值。

1.4.3.1.7.3. 环境变量

可以通过设置名为TF_VAR_<NAME>的环境变量为输入变量赋值,例如:

$ export TF_VAR_image_id=ami-abc123
$ terraform plan
...

在环境变量名大小写敏感的操作系统上,Terraform要求环境变量中的\与Terraform代码中定义的输入变量名大小写完全一致。

环境变量传值非常适合在自动化流水线中使用,尤其适合用来传递敏感数据,类似密码、访问密钥等。

1.4.3.1.7.4. 交互界面传值

在前面介绍断言的例子中我们看到过,当我们从命令行界面执行terraform操作,Terraform无法通过其他途径获取一个输入变量的值,而该变量也没有定义默认值时,Terraform会进行最后的尝试,在交互界面上要求我们给出变量值。

1.4.3.1.8. 输入变量赋值优先级

当上述的赋值方式同时存在时,同一个变量可能会被赋值多次。Terraform会使用新值覆盖旧值。

Terraform加载变量值的顺序是:

  1. 环境变量
  2. terraform.tfvars文件(如果存在的话)
  3. terraform.tfvars.json文件(如果存在的话)
  4. 所有的.auto.tfvars或者.auto.tfvars.json文件,以字母顺序排序处理
  5. 通过-var或是-var-file命令行参数传递的输入变量,按照在命令行参数中定义的顺序加载

假如以上方式均未能成功对变量赋值,那么Terraform会尝试使用默认值;对于没有定义默认值的变量,Terraform会采用交互界面方式要求用户输入一个。对于某些Terraform命令,如果执行时带有-input=false参数禁用了交互界面传值方式,那么就会报错。

1.4.3.1.9. 复杂类型传值

通过参数文件传值时,可以直接使用HCL或是JSON语法对复杂类型传值,例如list或map。

对于某些场景下必须使用-var命令行参数,或是环境变量传值时,可以用单引号引用HCL语法的字面量来定义复杂类型,例如:

export TF_VAR_availability_zone_names='["us-west-1b","us-west-1d"]'

由于采用这种方法需要手工处理引号的转义,所以这种方法比较容易出错,复杂类型的传值建议尽量通过参数文件。

results matching ""

    No results matching ""