共计 5754 个字符,预计需要花费 15 分钟才能阅读完成。
《Terraform 101 从入门到实际》这本小册在南瓜慢说官方网站和 GitHub 两个中央同步更新,书中的示例代码也是放在 GitHub 上,不便大家参考查看。
介绍了 Terraform 一些比拟根底的概念后,咱们能够先理解一下 Terraform 的语法,也就是 HCL 的语法。
变量 Variables
变量是实现代码复用的一种形式,同样的代码不同的变量往往会有不同的成果。而在 Terraform 里,有一个概念十分重要,就是变量都是从属于模块的。变量无奈跨模块援用。即在模块 A 定义的变量 X,无奈在模块 B 中间接援用。但父模块的变量,能够作为子模块的入参;而子模块的输入变量能够被父模块获取。
变量类型
从语言角度
跟任何编程语言一样,变量都是有类型的,Terraform 的变量类型从语言的角度可分为两大类:根本类型和组合类型,具体如下:
根本类型:
- 字符串 string,如
"pkslow.com"
- 数字 number,如
319
或5.11
- 布尔值 bool,如
true
组合类型:
- 列表 list(<T>),如
["dev", "uat", "prod"]
- 汇合 set(<T>),如
set(...)
- 映射 map(<T>),如
{name="Larry", age="18"}
- 对象 object({name1=T1, name2=T2})
- 元组 tuple([T1,T2,T3…])
如果不想指定某个类型,能够用 any
来示意任意类型;或者不指定,默认为任意类型。
从性能角度
从性能角度来看,变量能够分为输出变量、输入变量和本地变量。
输出变量是模块接管内部变量的形式,它定义在 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"
}
]
}
输入变量定义了一个模块对外返回的变量,通过 output
块来定义,如下:
output "instance_ip_addr" {value = aws_instance.server.private_ip}
本地变量是模块内定义且可援用的长期变量,在 locals
块中定义,如下:
locals {
service_name = "forum"
owner = "Community Team"
}
输出变量 Input Variable
输出变量是定义在 variable
块中的,它就像是函数的入参。
定义输出变量
定义 variable
有很多可选属性:
- 类型 type:指定变量是什么类型;如果没有指定,则能够是任意类型;
- 默认值 default:变量的默认值,定义后能够不必提供变量的值,留神它的值的类型要与 type 对应上;
- 阐明 description:阐明这个变量的作用和用处;
- 校验 validation:提供校验逻辑来判断输出的变量是否非法;
- 敏感性 sensitive:定义变量是否敏感,如果是则不会显示;默认为
false
; - 可空 nullable:如果为 true 则能够为空,否则不能。默认为
true
。
所有属性都显性指定如上面例子所示:
variable "env" {
type = string
default = "dev"
description = "environment name"
sensitive = false
nullable = false
validation {condition = contains(["dev", "uat", "prod"], var.env)
error_message = "The env must be one of dev/uat/prod."
}
}
这个变量名为 env
,示意环境名,默认值为dev
,这个值必须为dev
、uat
和prod
中的其中一个。如果输入一个非法的值,会报错:
$ terraform plan -var="env=sit"
╷
│ Error: Invalid value for variable
│
│ on input.tf line 1:
│ 1: variable "env" {
│
│ The env must be one of dev/uat/prod.
应用输出变量
只有定义了变量才能够应用,应用的形式是 var.name
。比方这里定义了两个变量env
和random_string_length
:
variable "env" {
type = string
default = "dev"
}
variable "random_string_length" {
type = number
default = 10
}
则应用如下:
resource "random_string" "random" {
length = var.random_string_length
lower = true
special = false
}
locals {instance_name = "${var.env}-${random_string.random.result}"
}
output "instance_name" {value = local.instance_name}
传入变量到根模块
要从内部传入变量到根模块,有多种形式,常见的有以下几种,按优先级从低到高:
- 环境变量
export TF_VAR_image_id=ami-abc123
terraform.tfvars
文件;terraform.tfvars.json
文件;*.auto.tfvars
或*.auto.tfvars.json
文件;- 命令行参数
-var
传入一个变量;命令行参数-var-file
传入一个变量的汇合文件;
在实践中,最罕用的还是通过命令行来传入参数,因为个别须要指定不同环境的特定变量,所以会把变量放到文件中,而后通过命令行指定特定环境的主文件:
$ terraform apply -var="env=uat"
$ terraform apply -var-file="prod.tfvars"
而 prod.tfvars
的内容如下:
env = "prod"
random_string_length = 12
咱们能够定义 dev.tfvars
、uat.tfvars
和prod.tfvars
等,要应用不同环境的变量就间接扭转文件名即可。
输入变量 Output Variable
有输出就有输入,输入变量就像是模块的返回值,比方咱们调用一个模块去创立一台服务,那就要获取服务的 IP,这个 IP 当时是不晓得,它是服务器创立完后的后果之一。输入变量有以下作用:
- 子模块的输入变量能够裸露一些资源的属性;
- 根模块的输入变量能够在 apply 后输入到控制台;
- 根模块的输入变量能够通过
remote state
的形式共享给其它 Terraform 配置,作为数据源。
定义输入变量
输入变量须要定义在 output
块中,如下:
output "instance_ip_addr" {value = aws_instance.server.private_ip}
这个 value
能够是 reource 的属性,也能够是各种变量计算后的后果。只有在执行 apply 的时候才会去计算输入变量,像 plan 是不会执行计算的。
还能够定义输入变量的一些属性:
description
:输入变量的形容,阐明分明这个变量是干嘛的;sensitive
:如果是true
,就不会在控制台打印进去;depends_on
:显性地定义依赖关系。
残缺的定义如下:
output "instance_ip_addr" {
value = aws_instance.server.private_ip
description = "The private IP address of the main server instance."
sensitive = false
depends_on = [
# Security group rule must be created before this IP address could
# actually be used, otherwise the services will be unreachable.
aws_security_group_rule.local_access,
]
}
援用输入变量
援用输入变量很容易,表达式为 module.<module name>.<output name>
,如果后面的输入变量定义在模块pkslow_server
中,则援用为:module.pkslow_server.instance_ip_addr
。
本地变量 Local Variable
本地变量有点相似于其它语言代码中的局部变量,在 Terraform 模块中,它的一个重要作用是防止反复计算一个值。
locals {instance_name = "${var.env}-${random_string.random.result}-${var.suffix}"
}
这里定义了一个本地变量 instance_name
,它的值是一个简单的表达式。这时咱们能够通过local.xxx
的模式援用,而不必再写简单的表达式了。如下:
output "instance_name" {value = local.instance_name}
这里要特地留神:定义本地变量的关键字是 locals
块,外面能够有多个变量;而援用的关键字是local
,并没有s
。
个别咱们是倡议须要反复援用的简单的表达式才应用本地变量,不然太多本地变量就会影响可读性。
对变量的援用
定义了变量就须要对其进行援用,后面的解说其实曾经讲过了局部变量的援用,这些把所有列出来。
类型 | 援用形式 |
---|---|
资源 Resources | <Resource Type>.<Name> |
输出变量 Input Variables | var.<NAME> |
本地变量 Local Values | local.<NAME> |
子模块的输入 | module.<Module Name>.<output Name> |
数据源 Data Sources | data.<Data Type>.<Name> |
门路和 Terraform 相干 | path.module :模块所在门路path.root :根模块的门路path.cwd :个别与根模块雷同,其它高级用法除外terraform.workspace :工作区名字 |
块中的本地变量 | count.index :count 循环的下标;each.key /each.value :for each 循环的键值;self :在 provisioner 的援用; |
下面都是单值的援用,如果是 List 或 Map 这种简单类型,就要应用中括号 []
来援用。
aws_instance.example[0].id
:援用其中一个元素;
aws_instance.example[*].id
:援用列表的所有 id 值;
aws_instance.example["a"].id
:援用 key 为 a
的元素;
[for value in aws_instance.example: value.id]
:返回所有 id 为列表;
运算符
与其它语言一样,Terraform 也有运算符能够用,次要是用于数值计算和逻辑计算。以下运算符按优先级从高到低如下:
!
取反,-
取负*
乘号,/
除号,%
取余+
加号,-
减号>
,>=
,<
,<=
:比拟符号==
等于,!=
不等于&&
与门||
或门
当然,用小括号能够扭转这些优良级,如(1 + 2) * 3
。
留神:对于结构化的数据比拟须要留神类型是否统一。比方 var.list == []
按理说应该返回 true
,而list
为空时。当 []
理论示意是元组 tuple([])
,所以它们不匹配。能够应用length(var.list) == 0
的形式。
条件表达式
条件表达式的作用是在两个值之间选一个,条件为真则选第一个,条件为假则选第二个。模式如下:
condition ? true_value : false_value
示例如下:
env = var.env !=""? var.env :"dev"
意思是给 env
赋值,如果 var.env
不为空就把输出变量 var.env
的值赋给它,如果为空则赋默认值dev
。
for 表达式
应用 for
表达式能够创立一些简单的值,而且能够应用一些转换和计算对值计算再返回。如将字符串列表转化成大写:
> [for s in ["larry", "Nanhua", "Deng"] : upper(s)]
[
"LARRY",
"NANHUA",
"DENG",
]
能够获取下标和值:
> [for i,v in ["larry", "Nanhua", "Deng"] : "${i}.${v}"]
[
"0.larry",
"1.Nanhua",
"2.Deng",
]
对于 Map 的 for 表达式:
> [for k,v in {name: "Larry Deng", age: 18, webSite: "www.pkslow.com"} : "${k}: ${v}"]
[
"age: 18",
"name: Larry Deng",
"webSite: www.pkslow.com",
]
通过条件过滤数据:
> [for i in range(1, 10) : i*3 if i%2==0]
[
6,
12,
18,
24,
]
动静块 Dynamic Block
动静块的作用是依据变量反复某一块配置。这在 Terraform 是会遇见的。
resource "aws_elastic_beanstalk_environment" "tfenvtest" {
name = "tf-test-name"
application = "${aws_elastic_beanstalk_application.tftest.name}"
solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6"
dynamic "setting" {
for_each = var.settings
content {namespace = setting.value["namespace"]
name = setting.value["name"]
value = setting.value["value"]
}
}
}
比方这里的例子,就会反复 setting
块。反复的次数取决于 for_each
前面跟的变量。