《Terraform 101 从入门到实际》这本小册在南瓜慢说官方网站和GitHub两个中央同步更新,书中的示例代码也是放在GitHub上,不便大家参考查看。

介绍了Terraform一些比拟根底的概念后,咱们能够先理解一下Terraform的语法,也就是HCL的语法。

变量Variables

变量是实现代码复用的一种形式,同样的代码不同的变量往往会有不同的成果。而在Terraform里,有一个概念十分重要,就是变量都是从属于模块的。变量无奈跨模块援用。即在模块A定义的变量X,无奈在模块B中间接援用。但父模块的变量,能够作为子模块的入参;而子模块的输入变量能够被父模块获取。

变量类型

从语言角度

跟任何编程语言一样,变量都是有类型的,Terraform的变量类型从语言的角度可分为两大类:根本类型和组合类型,具体如下:

根本类型:

  • 字符串string,如"pkslow.com"
  • 数字number,如3195.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,这个值必须为devuatprod中的其中一个。如果输入一个非法的值,会报错:

$ 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。比方这里定义了两个变量envrandom_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.tfvarsuat.tfvarsprod.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 Variablesvar.<NAME>
本地变量Local Valueslocal.<NAME>
子模块的输入module.<Module Name>.<output Name>
数据源Data Sourcesdata.<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. +加号,-减号
  4. >>=<<=:比拟符号
  5. ==等于,!=不等于
  6. &&与门
  7. ||或门

当然,用小括号能够扭转这些优良级,如(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前面跟的变量。