个人技术分享

使用输入变量可以在创建基础设施资源时动态传入值,如果把Terraform代码看作是一个函数,那么输入变量都是函数参数。

Terraform输入变量使用variable块进行定义,如下示例:

variable "image_id" {
    type = string  # 变量类型为字符串
}

variable "availability_zone_names" {
    type = list(string) # 变量类型为列表,列表元素为字符串类型
    default = ["us-west-1a"] # 变量默认值
}

variable "docker_ports" {
    type = list(object({ # 变量类型为列表,列表元素为对象类型
        interval = number,
        external = number,
        protocal = string
    }))

    // 设置变量默认值
    default = [
        {
            interval = 8300
            external = 8300
            protocal = "tcp"
        }
    ]
}

如上示例都是合法的变量定义,紧跟在variable关键字之后的就是变量名。
在同一个Terraform模块(不包含子文件夹)中的变量名必须是唯一的,在代码中可以使用var.变量名的方式来引用变量的值。
如下关键字不能作为输入变量名:sourceversionproviderscountfor_eachlifecycledepends_onlocals

输入变量名只能在声明该变量的目录下的代码中使用,在输入变量的定义中可以指定一些属性。

输入变量类型

在输入变量块中使用type指定类型,如:

variable "name" {
    type = string # 变量类型为字符串
}

variable "ports" {
    type = list(number) # 变量类型为列表,列表元素为数字
}

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

输入变量默认值

当Terraform无法获取一个输入变量的值时会使用其定义的默认值,如:

variable "name" {
    type = string # 变量类型为字符串
    default = "zhangsan" # 变量默认值
}

输入变量描述

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

variable "imag_id" {
    type = string # 变量类型为字符串
    description = "This id of the machine image (AMI) to use the server."
}

如果在执行terraform planterraform apply时Terraform不知道某个输入变量的值,Terraform会在命令行界面上提示为输入变量设置一个值,这时候给用户展示的提示信息就是定义的输入变量描述。

注:输入变量描述并不是注释信息!

输入变量的断言

输入变量的断言是在Terraform 0.13.0开始引入的,在输入变量定义块中通过validation块定义。

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-\"."
    }
}

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

可以使用can函数来判定表达式的执行是否会出错,如下示例:

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

    validation {
        condition = can(regex("^ami-", var.image_id)) # 使用can函数来判断表达式是否会执行出错
        error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
    }
}

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

在命令行输出中隐藏值

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

将输入变量设置为sensitive可以防止在执行terraform planterraform apply时将输入变量的值展示出来。

声明一个包含敏感数据值的输入变量需要将其sensitive属性设置为true,如下:

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

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

如果一个输入变量被声明为是敏感的,则任何使用敏感变量的表达式都将被视为敏感的。因此在上述例子中的资源”a”的两个参数nameaddress值也将在执行terraform planterraform apply时被隐藏。

可能暴露敏感数据值的地方

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

如果将资源属性用作,或是作为Provider定义的资源ID的一部分,则在执行terraform apply时将会公开该值。

+ 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]

在上述示例中,资源属性prefix已经设置为sensitive敏感类型,但随后该值(“jae”)作为资源ID的一部分被公开展示了。

禁止输入变量为空

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

输入变量的nullable参数控制调用者是否可以将null值赋值给该变量。

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

nullable的默认值为true,此时将null值赋值给输入变量将会覆盖其默认值;如果nullablefalse且输入变量有默认值,当把null赋值给输入变量时,Terraform将使用输入变量的默认值。
nullable参数仅仅控制输入变量的直接值可能为null值的情况,对于集合或对象类型的输入变量,仍然可以在集合元素或属性中使用null值,只要集合或对象本身不为null值即可。

给输入变量赋值

对输入变量赋值有多种方式,如:命令行参数,参数文件,环境变量,交互界面传值。

命令行参数

在Terraform命令行中使用-var="输入变量名=值"的方式给输入变量赋值。

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

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

参数文件

参数文件的后缀名可以是.tfvars.tfvars.json.tfvars文件使用HCL语法,.tfvars.json文件使用JSON语法。
.tfvars文件为例,用HCL代码给输入变量赋值:

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

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

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

然后在Terraform命令中使用-var-file参数指定参数文件:

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

有2种情况可以不用明确指定参数文件(Terraform会自动使用这2种情况下的参数文件):

  • 当前模块内存在名为terrform.tfvarsterraform.tfvars.json的文件
  • 当前模块内存在一个或多个后缀名为.auto.tfvars.auto.tfvars.json的文件

环境变量

可以设置名为TF_VAR_输入变量名的环境变量名为输入变量赋值:

$ export TF_VAR_image_id=ami-abc123
$ terraform apply

环境变量传值特别适合在自动化流水线中使用,尤其适合用来传递敏感数据的场景。

交互界面传值

当从命令行界面执行Terraform操作,Terraform无法从其他途径获取输入变量的值时,而该输入变量又未定义默认值,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禁用了界面传值方式,那么将会报错。

给复杂类型的输入变量赋值

通过参数文件给定义的输入变量传值时,可以直接使用HCL语法或JSON语法对复杂类型的输入变量传值(例如:list或map)。
对于某些场景下必须使用-var命令行参数,或者环境变量传值时,可以使用单引号引用HCL语法的字面量来定义复杂类型,如:

# 采用这种方式需要手动处理引号的转义,比较容易出错
$ export TF_VAR_available_zone_names='["us-west-1d", "us-west-1b"]'

复杂类型的传值建议使用参数文件。