《Terraform 101 从入门到实践》 第五章 HCL语法
《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看。
介绍了Terraform一些比较基础的概念后,我们可以先了解一下Terraform的语法,也就是HCL的语法。
变量Variables
变量是实现代码复用的一种方式,同样的代码不同的变量往往会有不同的效果。而在Terraform里,有一个概念非常重要,就是变量都是从属于模块的。变量无法跨模块引用。即在模块A定义的变量X,无法在模块B中直接引用。但父模块的变量,可以作为子模块的入参;而子模块的输出变量可以被父模块获取。
变量类型
从语言角度
跟任何编程语言一样,变量都是有类型的,Terraform的变量类型从语言的角度可分为两大类:基本类型和组合类型,具体如下:
基本类型:
- 字符串string,如
"pkslow.com"
- 数字number,如
319
或5.11
- 布尔值bool,如
true
组合类型:
- 列表list(),如
["dev", "uat", "prod"]
- 集合set(),如
set(...)
- 映射map(),如
{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
后面跟的变量。
《Terraform 101 从入门到实践》 第五章 HCL语法的更多相关文章
- 《Terraform 101 从入门到实践》 第一章 Terraform初相识
<Terraform 101 从入门到实践>这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看. 初闻不知Terraform, ...
- 《Terraform 101 从入门到实践》 第二章 Providers插件管理
<Terraform 101 从入门到实践>这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看. 不怕出身低,行行出状元. 插 ...
- 《Terraform 101 从入门到实践》 第三章 Modules模块化
<Terraform 101 从入门到实践>这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看. 模块的概念 模块化是Terr ...
- 《Terraform 101 从入门到实践》 第四章 States状态管理
<Terraform 101 从入门到实践>这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看. 军书十二卷,卷卷有爷名. 为 ...
- python编程:从入门到实践----第五章>if 语句
一.一个简单示例 假设有一个汽车列表,并想将其每辆汽车的名称打印出来.遇到汽车名‘bmw’,以全大写打印:其他汽车名,首字母大写 cars=['audi','bmw','subaru','toyota ...
- Python:从入门到实践--第五章--if语句--练习
#1.编写一系列条件测试:将每个测试以及结果打印出来 car = '宝马' if car == "宝马": print("预测正确") print(car) e ...
- python编程:从入门到实践----第五章:if语句>练习
5-1 条件测试 :编写一系列条件测试:将每个测试以及你对其结果的预测和实际结果都打印出来. a. 详细研究实际结果,直到你明白了它为何为True 或False b. 创建至少2个测试,且其中结果分别 ...
- #Python编程从入门到实践#第四章笔记
#Python编程从入门到实践#第四章笔记 操作列表 1.遍历列表 使用for循环,遍历values列表 for value in values: print(value) 2.数字列表 使 ...
- ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区
原文:ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区 1 ArcCatalog使用 1.1 GIS数据 地理信息系统, ...
- D3.js的v5版本入门教程(第五章)—— 选择、插入、删除元素
D3.js的v5版本入门教程(第五章) 1.选择元素 现在我们已经知道,d3.js中选择元素的函数有select()和selectAll(),下面来详细讲解一下 假设我们的<body>中有 ...
随机推荐
- KeeWiDB的高性能修炼之路:架构篇
数据也有冷热之分,你知道吗? 根据访问的频率的高低可将数据分为热数据和冷数据,访问频率高的则为热数据,低为冷数据.如果热.冷数据不区分,一并存储,显然不科学.将冷数据也存储在昂贵的内存中,那么你想,成 ...
- Dubbo-时间轮设计
前言 Dubbo源码阅读分享系列文章,欢迎大家关注点赞 SPI实现部分 Dubbo-SPI机制 Dubbo-Adaptive实现原理 Dubbo-Activate实现原理 Dubbo SPI-Wrap ...
- Python调用golang
有些时候因为效率问题部分代码会 使用Python调用go的编译生成动态链接库go 代码示例//add.gopackage main import "C" //export Addf ...
- 8、将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数。如果s1 = s2,输出零。如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是比较两字符串相应字符的ascii码的差值。
/* 将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数.如果s1 = s2,输出零.如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是 ...
- UED Landing 页 - 定时抓取掘金文章
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:琉易 https://liuxianyu.cn 本次分享基 ...
- 基于.NET 7 的 WebTransport 实现双向通信
Web Transport 简介 WebTransport 是一个新的 Web API,使用 HTTP/3 协议来支持双向传输.它用于 Web 客户端和 HTTP/3 服务器之间的双向通信.它支持通过 ...
- flutter系列之:在flutter中使用流式布局
目录 简介 Flow和FlowDelegate Flow的应用 总结 简介 我们在开发web应用的时候,有时候为了适应浏览器大小的调整,需要动态对页面的组件进行位置的调整.这时候就会用到flow la ...
- Kubernetes专栏 | 安装部署(一)
--随着云原生概念的普及,许多企业的业务纷纷上云,为了追求可靠性,稳定性,和弹性伸缩,提升资源利用率等需求.Kubernetes这个谷歌开源的容器编排平台已日益流行,被大家熟知和使用. 通常来说,Ku ...
- Linux 基础-新手必备命令
Linux 基础-新手必备命令 概述 常见执行 Linux 命令的格式是这样的: 命令名称 [命令参数] [命令对象] 注意,命令名称.命令参数.命令对象之间请用空格键分隔. 命令对象一般是指要处理的 ...
- Kafka教程(三):原理及存储
一.思维导图 1.实时更新连接 https://www.mubucm.com/doc/1GRE2U7qYuj 2.思维导图图片 二.具体内容 8.系统架构 架构推导 拓扑结构 多对多 ...