本文介绍一下 Terraform 配置代码的基础语法。
通过对本文的学习,能更快的、更高效的了解并使用 Terraform。
基础语法
Terraform 配置语言主要基于 HCL 语法,具有配置简单,可读性强等特点,并且兼容 JSON 语法。
Terraform 配置语言主要由参数 (Argument),块 (Block),表达式 (Experssion) 和函数 (Functions) 组成。
参数 (Argument)
使用等号 (=) 将一个值或表达式赋值给指定的参数名称,参数名称可以使用字母、数字、 下划线 (_) 和连接符 (-) 表示,且首字母不能是数字,例如:
image_id = "ad091b52-742f-469e-8f3c-fd81cadf0743"
块 (Block)
块将多个参数聚合在一起,并支持嵌套。
块由块类型、块标签和块主体构成,格式如下:
resource "huaweicloud_compute_instance" "myinstance" {
name = "myinstance"
......
network {
uuid = "55534eaa-533a-419d-9b40-ec427ea7195a"
}
}
在使用块时必须先声明其对应的类型,样例中 resource 和 network 均为块类型,其中 resource 为顶层块类型,network 为嵌套块类型。
Terraform 支持的顶层块类型包括: provider,resource,data,variable,output,module,locals 等关键字。
块标签在块类型之后定义,且数量由块类型决定,样例中 resource 块类型包含两个标签:huaweicloud_compute_instance 和 myinstance,嵌套的 network 类型没有块标签。
块主体定义在块最后,由 { 和 } 字符进行封装,在块主体内可以嵌套其他类型以实现不同的层级结构。
更多详细信息,请参见 Terraform 的配置语法文档。
参数类型
Terraform支持以下参数类型:
基本类型
- string:字符串类型,由一个或多个 Unicode 字符组成,例如 "hello"。
- number:数字类型,可以表示整数和浮点数。
- bool:布尔类型,只能是 true 或 false。
Terraform 能够根据参数类型自动将 number 和 bool 类型转换为 string 类型。
如果一个字符串能够表示为一个数字或布尔类型的值,Terraform 也可以进行反向转换。
字符串、 数字和布尔类型的参数可以直接赋值,例如:
disk_type = "SSD"
disk_size = 40
enable = true
# 支持使用字符串表示数字和布尔类型
disk_size = "40"
enable = "true"
集合类型
- map (…):映射类型,以键值对 (key-value pair) 的方式组合起来的数据元素集合,其中 key 为 string 类型,对应的值可以是 string,number,bool 等类型,且所有元素的值必须是同一类型。
- list (…):列表类型,具有同类型的数据元素集合,元素可以是基本类型和块类型,列表索引从 0 开始。
- set (…):集合类型,类似于列表类型,但是集合中的元素是没有任何辅助标识符或顺序,且元素具有唯一性。
映射类型使用 {} 封装,其表示形式非常灵活:键值对可以使用等号 "=" 或冒号 ":" 连接;如果 key 不以数字开头,可以不加双引号;对于多行映射,键值对之间可以使用换行符或者逗号进行分隔。
推荐使用等号 (=) 连接键值对并用换行符进行分隔,例如:
# 推荐格式
tags = {
foo = "bar"
key = "value"
}
# 其他格式
tags = {"foo" = "bar", "key" = "value"}
tags = {"foo" : "bar", "key" : "value"}
tags = {foo = "bar", key = "value"}
tags = {foo : "bar", key : "value"}
tags = {
foo : "bar"
key : "value"
}
列表类型和集合类型的表示方式相同,其中元素为基本类型的列表/集合使用 [] 封 装,元素为块类型的列表/集合使用重复块的形式表示,例如:
# 基本类型的列表
security_groups = ["default", "internal"]
# 块类型的列表
network {
uuid = "55534eaa-533a-419d-9b40-ec427ea7195a"
}
network {
uuid = "ad091b52-742f-469e-8f3c-fd81cadf0743"
}
特殊类型
- null:空类型,如果将一个参数设置为 null,表示这个参数未填写,Terraform 会自动忽略该参数,并使用默认值。null 在条件表达式中较为常见,如 var.test=="" ? null : var.test,表示当 var.test 的值为 "" 时,就将其忽略。
其他语法
-
单行注释以 # 或 // 开头;
-
多行注释 /* 开始,以 */ 结束,不支持嵌套块注释。
-
Terraform 配置文件使用 UTF-8 编码,对于标识符、注释和字符串都支持非 ASCII 字符。
-
多行字符串在一行末尾以
<<EOF
开头,中间是字符串内容,最后以EOF
结尾。EOF
也可以替换为其他字符。例如:
resource "huaweicloud_obs_bucket" "web_bucket" {
...
website {
...
routing_rules = <<EOF
[{
"Condition": {
"KeyPrefixEquals": "docs/"
},
"Redirect": {
"ReplaceKeyPrefixWith": "documents/"
}
}]
EOF
}
}
样式约定
Terraform 约定了一些惯用的风格样式,以确保不同团队编写的文件和模块的风格一致性。
建议用户遵循这些约定,推荐的样式约定如下:
-
对于每个嵌套级别,缩进两个空格。
-
当多个单行的参数在同一嵌套级别连续出现时,建议将等号对齐。
name = "myinstance" availability_zone = "cn-north-1a"
-
使用空行分隔块中的逻辑参数组。
-
当块主体同时包含参数和块时,建议将所有参数放在顶部,嵌套块放在参数的下方并使用空行隔开。
-
将元参数 (meta-arguments) 放在块主体的顶部,并使用空行与其它参数隔开;将元参数块 (meta-argument blocks) 放在块主体的最后,并用空行与其他块隔开。
resource "huaweicloud_obs_bucket" "demo" { count = 1 bucket = "bucket_demo" acl = "public-read" tags = { foo = "bar" env = "test" } lifecycle { create_before_destroy = true } }
-
顶层块之间使用空行将彼此隔开。
-
建议将相同类型的嵌套块放在一起,不同类型的嵌套块使用空行隔开。
参考资料
https://www.terraform.io/docs/configuration/style.html
表达式
表达式用于引用或计算配置中的值,最简单的表达式是文字表达式,如 "hello world" 或 5。
Terraform 支持多种表达式,包括运算符、条件表达式以及丰富的内置函数。
通过 "terraform console" 命令可以打开一个交互式的控制台,我们可以使用该控制台进行表达式及内置函数的体验和测试。
运算符
运算符是执行特定的数学或逻辑操作的服务,Terraform 支持以下类型的运算符:
- 算术运算符:操作数和结果都为数字类型,包括:+,-(减法),*,/,%,-(负数)。
- 关系运算符:操作数为任意类型,结果为布尔值,包括:==,!=。
- 比较运算符:操作数为数字类型,结果为布尔值,包括:>,>=,<,<=。
- 逻辑运算符:操作数和结果都为布尔类型,包括:||,&&,!。
在表达式中使用多个运算符时,将按照以下优先级进行求解:
- !, – (负数)
- *, /, %
- +, – (减法)
- >, >=, <, <=
- ==, !=
- &&
- ||
条件表达式
条件表达式采用布尔表达式的值进行二选一,其语法可以表示为:
condition ? true_value : false_value
该语句表示:如果 condition 为true,结果为 true_value,否则为 false_value。条件表达式的结果可以是任意类型,但 true_value 和 false_value 的类型必须保持一致。
条件表达式的常见用法是使用默认值替换无效值,如下:
var.a != "" ? var.a : "default-a"
该语句表示:如果 var.a 的值不为空,则返回 var.a 的值,否则返回一个默认值。
for 表达式
for 表达式用于遍历集合类型 (map、list、set) 中的每个元素,并对元素进行处理,最 后将结果输出为一个新的集合类型。
for 表达式的输出结果取决于所使用的括号类型:
- 使用 ‘[‘ 和 ‘]’ 将生成一个列表
- 使用 ‘{‘ 和 ‘}’ 将生成一个映射/对象
假设列表 mylist 的值为 ["AA", "BBB", "CCCC"],我们可以使用for表达式对 mylist 中的每个字符串元素转换为小写,并输出一个列表:
> [for str in var.mylist : lower(str)]
[
"aa",
"bbb",
"cccc",
]
我们也可以将结果输出为一个映射,映射关系通过 "=>" 确定:
> {for str in var.mylist : str => lower(str)}
{
"AA" = "aa"
"BBB" = "bbb"
"CCCC" = "cccc"
}
映射类型也可以通过 for 表达式转换进行处理,假设 mymap 的值为 {element1="aaa", element2="bbb", element3="ccc"},我们可以将映射中的每个键值转换为大写:
> {for key, value in var.mymap : key => upper(value)}
{
"element1 = "AAA"
"element2 = "BBB"
"element3 = "CCC"
}
此外,for 表达式还可以使用 if 语句对元素进行过滤:
> [for str in var.list : upper(str) if length(str) >= 3]
[
"bbb",
"cccc",
]
参考资料
https://www.terraform.io/docs/configuration/expressions.html
常见函数
Terraform 支持丰富的内置函数,用于处理字符串、数值计算、加密,类型转换等操作,我们可以通过函数名称进行调用,其语法如下:
<函数名称>(<参数1>, <参数2> ...)
本文主要对 Terraform 中常见的函数进行总结并通过样例说明其用法。
通过 Terraform 官方文档可以查看完整的函数支持列表。
字符串函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
format | 字符串格式化 | format("Hello, %s!", "Huaweicloud") | Hello, Huaweicloud! |
lower | 将字符串中的字母转换为小写 | lower("HELLO") | hello |
upper | 将字符串中的字母转换为大写 | upper("hello") | HELLO |
join | 使用自定义字符将列表拼接成字符串 | join(", ", ["One", "Two", "Three"]) | One, Two, Three |
split | 根据分隔符拆分字符串 | split(", ", "One, Two, Three") | ["One", "Two", "Three"] |
substr | 通过偏移量和长度从给定的字符串中提取一个子串 | substr("hello world!", 1, 4) | ello |
replace | 把字符串中的 str1 替换成 str2 | replace("hello, huaweicloud!", "h", "H") | Hello, Huaweicloud! |
数值计算函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
abs | 计算绝对值 | abs(-12.4) | 12.4 |
max | 计算最大值 | max(12, 54, 6),max([12, 54, 6]…) | 54, 54 |
min | 计算最小值 | min(12, 54, 6),min([12, 54, 6]…) | 6, 6 |
log | 计算对数 | log(16, 2) | 4 |
power | 计算x的y次幂 | power(3, 2) | 9 |
集合函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
element | 通过下标从列表中检索对应元素值 | element(["One", "Two", "Three"], 1) | Two |
index | 返回给定值在列表中的索引,如果该值不存在将报错 | index(["a", "b", "c"], "b") | 1 |
lookup | 使用给定的键从映射表中检索对应的值。如果给定的键不存在,则返回默认值 | lookup({IT="A", CT="B"}, "IT", "G") ,lookup({IT="A", CT="B"}, "IE", "G") | A, G |
flatten | 展开列表中的嵌套元素 | flatten([["a", "b"], [], ["c"]] | ["a", "b", "c"] |
keys | 返回 map 中的所有 key | keys({a=1, b=2, c=3}) | ["a", "b", "c"] |
length | 获取列表、映射或是字符串的长度 | length(["One", "Two", "Three"]),length({IT="A", CT="B"}),length("Hello, Huaweicloud!") | 3, 2, 19 |
类型转化函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
toset | 将列表类型转换为集合类型 | toset(["One", "Two", "One"]) | ["One", "Two"] |
tolist | 将集合类型转换为列表类型 | toset(["One", "Two", "Three"]) | ["One", "Two", "Three"] |
tonumber | 将字符串类型转换为数字类型 | tonumber("33") | 33 |
tostring | 将数字类型转换为字符串类型 | tostring(33) | "33" |
编码函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
base64en code | 将 UTF-8 字符串转换为 base64 编码 | base64encode("Hello, Huaweicloud!") | SGVsbG8sIEh1YXdlaWNsb3VkIQ== |
base64de code | 将 base64 编码解码为 UTF-8 字符串(结果非UTF-8格式会报错) | base64decode("SGVsbG8sIEh1YXdlaWNsb3VkIQ==") | Hello, Huaweicloud! |
base64gzi p | 将UTF-8字符串压缩 并转换为base64编码 | base64gzip("Hello, Huaweicloud!") | H4sIAAAAAAAA//JIzcnJ11HwKE0sT81MzskvTVEEAAAA//8BAAD//7SZqpwTAAAA |
哈希和加密函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
sha256 | 计算字符串的 SHA256 值(16进 制) | sha256("Hello, Huaweicloud!") | bbb76b2eb48a6610c1c87c8828c9b22ee1a5f5ca4c5c91584be154def9404910 |
sha512 | 计算字符串的 SHA512 值(16进 制) | sha512("Hello, Huaweicloud!") | 61f1ce05848b7dd7b23ee6ed5f32d9ce7476066d1c4c7a2f6a8f9d51f8edabb569ab22af0c796d01b6291715eb844edaafb1da4d6b80ed343844 2016d42dac7b |
base64sha256 | 计算字符串的 SHA256 值,并转换为 base64 编码 | base64sha256("Hello, Huaweicloud!") | u7drLrSKZhDByHyIKMmyLuGl9cpMXJFYS+FU3vlASRA= |
base64sha512 | 计算字符串的 SHA512 值,并转换为 base64 编码 | base64sha512("Hello, Huaweicloud!") | YfH0BYSLfdeyPubtXzLZznR2Bm0cTHovao+dUfjtq7VpqyKvDHltAbYpFxXrhE7ar7HaTWuA7TQ4RCAW1C2sew== |
md5 | 计算 MD5 值 | md5("hello world") | 5eb63bbbe01eeed093cb22bb8f5acdc3 |
base64sha512("Hello, Huaweicloud!") 不等于 base64encode(sha512("Hello, Huaweicloud!")),因为 sha512 计算的十六进制值结果在 Terraform 中是 Unicode 编码格式,并没指定 UTF-8 实现。
文件操作函数
函数名称 | 函数描述 | 样例 | 运行结果 |
---|---|---|---|
abspath | 计算文件的绝对路径 | abspath("./hello.txt") | /home/demo/test/terraform/hello.txt |
dirname | 计算字符串中包含的路径 | dirname("foo/bar/baz.txt") | foo/bar |
basename | 计算字符串中的文件名 | basename("foo/bar/baz.txt") | baz.txt |
file | 读取文件并返回文件内容 | file("./hello.txt") | Hello, Huaweicloud! |
filebase64 | 读取文件并返回文件内容的base64编码 | filebase64("./hello.txt") | SGVsbG8sIEh1YXdlaWNsb3VkIQ== |