概括地说,Terraform的数据类型分为两种:原始类型,复杂类型。
原始类型
原始类型包含3个:string
,number
,bool
。
string
:表示一组Unicode字符,例如:”hello”number
:表示数字,可以为整数,也可以为小数bool
:表示布尔值,可选值为true或false,bool
类型可以做逻辑判断
number
和bool
都可以和string
进行隐式转换,当把number
或bool
赋值给string
时,或者反过来,Terraform都会自动做类型转换,如下示例:
- true会被转换为”true”,反之”true”会被转换为true
- false会被转换为”false”,反之”false”会被转换为false
- 15会被转换为”15”,反之”15”会被转换为15
复杂类型
复杂类型是一组值所组成的复合类型,有2类复杂类型。
集合类型
Terraform支持三种集合类型:
- 列表:list(…),列表是一组值的连续集合,可以用下标访问内部元素(从0开始)。list类型的声明可以是list(number),list(string),list(bool)等,括号中的类型即为列表元素类型。
- 字典:map(…),表示一组键值对的集合,键类型必须是
string
,值的类型任意。map(number)表示键为string
类型而值为number
类型的字典。map类型的声明方式有2种风格:一种是以类似{"foo":"bar", "bar":"baz"}
(键和值都以双引号包含);另一种是类似{foo="bar",bar="baz"}
(键不以双引号包含,但如果键是以数字开头的例外)。多个键值对以逗号分隔,也可以使用换行符分隔。推荐使用第二种风格(Terraform规范规定按等号对齐,使用等号会使得代码在格式化后更美观)。 - 集合:set(…),代表一组不重复的值的集合。
以上集合类型都支持通配类型缩写,如:list
等价于list(any)
,map
等价于map(any)
,set
等价于set(any)
。any
代表支持任意元素类型,前提是所有元素都必须是同一个类型。例如:将list(string)
赋值给list(any)
,将list(number)
赋值给list(any)
都是合法的。
结构化类型
一个结构化类型允许将多个不同类型的值组合成一个类型,结构化类型必须提供一个schema
结构信息作为参数来指明元素的结构。
Terraform支持两种结构化类型:
- 对象:object(…),对象是指一组具有名称和类型的属性所构成的复合类型,它的schema信息为:
{key=type,key=type,...}
。例如:object({name=string, age=number})代表由名称
为”name”类型为string
,以及名称为”age”类型为number
的两个属性组成的对象。赋值给object
类型的合法值必须包含所有属性值,但是可以拥有多余的属性(多余的属性在赋值时会被抛弃)。例如:对于object({name=string,age=number})来说,{age=18}
是一个非法值,而{name="zhangsan", age=18, gender="male"}
是一个合法值,但赋值时gender
属性会被丢弃。 - 元组:tuple(…),元组类似于list,也是一组值的连续集合,但是每个元素都可以有独立的类型。元组同列表一样,也可以通过下标访问元素(下标从0开始),元组的schema信息为:
[type, type, ...]
。元组的元素数量必须与其schema声明的类型数量相等,并且每个元素的类型都必须与其schema相应位置的类型相同。例如:tuple([string, number, bool])类型的一个合法值可以是["a", 15, true]
。
复杂类型也支持隐式类型转换。Terraform会尝试转换相似的类型,转换规则如下:
object
和map
:如果一个map的键集合含有object规定的所有属性,那么map可以转换被转换为object,map里多余的键值会被抛弃,由map -> object -> map
的转换可能存在数据丢失。tuple
和list
:当一个list元素的数量正好等于一个tuple声明的长度,list可以被转换为tuple。例如:值为["18", "true", "john"]
的list转换为type([number, bool, string])
的tuple结果为[18, true, “john”]。set
和tuple
:当一个list或者tuple被转换为一个set,重复的值将被丢弃,并且原值的顺序也将丢失。如果一个set被转换为list或者tuple,那么元素将按照以下顺序排列:如果set元素类型是string,那么将按照字母顺序排列,其他类型的元素不承诺任何特定的排列顺序。
复杂类型转换时,元素类型将在可能的情况下发生隐式转换,类似上述list到tuple的转换。
如果类型不匹配,Terraform将报错。例如:试图将object({name=["zhangsan", "lisit"],age=12})
转换为map类型,这是不合法的,因为name的值为list,无法转换为string。
any类型
any
类型是一种特殊的类型约束,它本身并非一个类型,而只是一个类型占位符。每当一个值被赋予由any
约束的复杂类型时,Terraform会尝试计算出一个最精确的类型来替代any。
例如:把[“a”, “b”, “c”]赋值给list(any),它在Terraform中实际的物理类型首先被编译成tuple([string, string, string]),然后Terraform认为tuple和list相似,所以会尝试将它转换为list(string)。Terraform发现list(string)
符合list(any)
的约束,所以会用string
取代any
,于是赋值的最终类型为list(string)。
由于即使是list(any),所有元素类型也必须是一样的,所以某些类型转换到list(any)时会对元素进行隐式类型转换。例如:["a", 1, "b"]
赋值给list(any),Terraform发现1
可以转换为"1"
,所以最终的值为:["a", "1", "b"]
。
声明类型时如果不想有任何的约束,可以使用any
:
variable "no_type_constraint" {
type = any # 这样Terraform可以将任何类型的数据赋值给参数
}
null类型
存在一种值是无类型的,那就是null
。null代表数据缺失,如果把一个参数设置为null,Terraform会认为你忘记为它赋值。如果该参数有默认值,Terraform会使用默认值。如果没有默认值该参数又是必填字段,Terraform会报错。null
在条件表达式中非常有用,可以在某项条件不满足时跳过对某参数的赋值。
object的optional成员
自Terraform 1.3开始,可以在object
类型定义中使用optional
修饰属性。
在1.3之前,如果一个variable
类型为object
,那么在赋值时必须传入一个完全相符的对象,如下所示:
variable "any_object" {
type = object({
a = string,
b = string,
c = number
})
}
如果想传入一个对象给var.any_object
,但是不准备给属性b
和c
赋值,比如这样做:
{
a = "1",
b = null,
c = null
}
传入的对象必须必须完全符合object
类型定义的结构,哪怕是不想对某些属性赋值。
Terraform 1.3允许为一个属性添加optional
声明:
variable "with_optional_attribute" {
a = string, # 必须属性
b = optional(string), # 可选属性
c = optional(number, 12) # 带默认值的可选属性,如果不传值则使用默认值
}
optional
修饰符有两个参数:
- 类型:(必填)第一个参数标明了属性的类型
- 默认值:(选填)第二个参数定义了Terraform在没有定义该属性值时使用的默认值,默认值必须与参数类型兼容。如果没有指定默认值,Terraform会使用
null
作为默认值。
示例1:带有optional修饰符和默认值的内嵌结构
如下例子演示了一个输入变量,用来描述一个存储了静态网站的存储桶。
该变量的类型包含了一系列的optional
属性,包括website
,不但其自身是optional
的,其内部还包含了数个optional
的属性和默认值。
variable "buckets" {
type = list(object({ # 列表元素类型为对象,对象中包含了3个属性
name = string,
enabled = optional(bool, true),
website = optional(object({
index_document = optional(string, "index.html"),
error_document = optional(string, "error.html"),
routing_rules = optional(string)
}))
}))
}
以下给出一个样例terraform.tfvars
文件,为var.buckets
定义了三个存储桶:
production
:enabled属性使用了默认值archived
:website属性使用了默认值docs
:enabled属性使用了默认值,website属性中的”routing_rules”也使用了默认值
buckets = [
{
name = "production",
website = {
routing_rules = <<-EOF
[
{
"Condition" = { "KeyPrefixEquals": "img/" },
"Redirect" = { "ReplaceKeyPrefixWith": "images/" }
}
]
EOF
}
},
{
name = "archived",
enabled = false
},
{
name = "docs",
website = {
index_document = "index.txt",
error_document = "error.txt"
}
}
]
上述配置会产生如下的varible
值:
tolist([
{
"enabled" = true
"name" = "production"
"website" = {
"error_document" = "error.html"
"index_document" = "index.html"
"routing_rules" = <<-EOT
[
{
"Condition" = { "KeyPrefixEquals": "img/" },
"Redirect" = { "ReplaceKeyPrefixWith": "images/" }
}
]
EOT
}
},
{
"enabled" = false
"name" = "archived"
"website" = {
"error_document" = "error.html"
"index_document" = "index.html"
"routing_rules" = tostring(null)
}
},
{
"enabled" = true
"name" = "docs"
"website" = {
"error_document" = "error.txt"
"index_document" = "index.txt"
"routing_rules" = tostring(null)
}
},
])
示例2:有条件地设置一个默认属性
有时需要根据其他数据的值来动态决定是否要为一个optional
参数设置值:这在引入Terraform的module
之后会比较常见,发起调用的module
块可以使用条件表达式搭配null
来动态决定是否应该设置该参数。
还是上面variable buckets
例子,使用下面的演示可以根据新输入的参数var.legacy_filenames
的值来有条件地覆盖website
对象中的index_document
和error_document
设置。
variable "legacy_filenames" {
type = bool
default = false
nullable = false
}
module "buckets" {
source = "./modules/buckets"
buckets = [
{
name = "maybe_legacy",
website = {
index_document = var.legacy_filenames ? "INDEX.html": null,
error_document = var.legacy_filenames ? "ERROR.html": null
}
}
]
}
当var.legacy_filenames
设置为true
时,才会覆盖websiet
属性的index_document
和error_document
值;当它为false时,调用不会指定website
属性的index_document
和error_document
值,这样website
属性的index_document
和error_document
会使用定义的默认值。
【参考】
Types and Values
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,在下面评论区告诉我^_^^_^