本篇介绍 Terraform syntax,为何 .tf 内容是这个格式
课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days
赛後文章会整理放到个人的部落格上 http://chechia.net/
写到现在,应该都可以写出能够工作的 terraform。
到目前为止,课程对学生的期待是『能够 google 到社群分享的 .tf,并且会正确的复制贴上,转化成自己的 .tf 内容』。
做到现在,想必也累积了一些问题。之前的内容,或许有些逻辑还不是很清楚,课程都简单带过,许多复杂的部分略过不提;目的先让学生对语言有个基础的了解,有实际操作的经验跟手感,之後做深入的讨论时,更能理解内容。
接下来要继续深入,我们会花一点篇幅,回头细讲细节,把关防文件细细地走过
先从这篇出发Terraform Syntax,介绍 Terraform syntax。这里又分为两部分:一个是Configuration Syntax,另一个是Json Configuration Syntax。乍看之下有点疑惑,json 是从哪跑出来的?我们可以从几个角度看这件事:
Terraform 底层 low-level syntax 是由 Hashicorp Configuration Language 定义的
这里要讲古一下:历史来看, hashicorp 在 2014 年发表 terraform,同时也释出 hcl 语言 spec
用范例讲解一下上面一大段,回头看 _poc/security_group/security_group.tf
使用 cli 的 global option -json 来输出 json
terraform plan
# azurerm_network_security_group.main will be created
+ resource "azurerm_network_security_group" "main" {
+ id = (known after apply)
+ location = "southeastasia"
+ name = "poc-chechia"
+ resource_group_name = "terraform-30-days"
+ security_rule = (known after apply)
+ tags = {
+ "environment" = "poc"
}
}
# azurerm_network_security_rule.main["homeport22"] will be created
+ resource "azurerm_network_security_rule" "main" {
+ access = "Allow"
+ destination_address_prefix = "*"
+ destination_port_range = "22"
+ direction = "Inbound"
+ id = (known after apply)
+ name = "Port_22"
+ network_security_group_name = "poc-chechia"
+ priority = 100
+ protocol = "*"
+ resource_group_name = "terraform-30-days"
+ source_address_prefix = "17.110.101.57"
+ source_port_range = "*"
}
terraform plan -json
{"@level":"info","@message":"azurerm_network_security_group.main: Plan to create","@module":"terraform.ui","@timestamp":"2021-08-18T22:58:26.459366+08:00","change":{"resource":{"addr":"azurerm_network_security_group.main","module":"","resource":"azurerm_network_security_group.main","implied_provider":"azurerm","resource_type":"azurerm_network_security_group","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"azurerm_network_security_rule.main[\"homeport22\"]: Plan to create","@module":"terraform.ui","@timestamp":"2021-08-18T22:58:26.461178+08:00","change":{"resource":{"addr":"azurerm_network_security_rule.main[\"homeport22\"]","module":"","resource":"azurerm_network_security_rule.main[\"homeport22\"]","implied_provider":"azurerm","resource_type":"azurerm_network_security_rule","resource_name":"main","resource_key":"homeport22"},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"Plan: 2 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2021-08-18T22:58:26.461227+08:00","changes":{"add":2,"change":0,"remove":0,"operation":"plan"},"type":"change_summary"}
# format with jq
{
"@level": "info",
"@message": "azurerm_network_security_rule.main[\"homeport22\"]: Plan to create",
"@module": "terraform.ui",
"@timestamp": "2021-08-18T22:58:26.461178+08:00",
"change": {
"resource": {
"addr": "azurerm_network_security_rule.main[\"homeport22\"]",
"module": "",
"resource": "azurerm_network_security_rule.main[\"homeport22\"]",
"implied_provider": "azurerm",
"resource_type": "azurerm_network_security_rule",
"resource_name": "main",
"resource_key": "homeport22"
},
"action": "create"
},
"type": "planned_change"
}
{
"@level": "info",
"@message": "Plan: 2 to add, 0 to change, 0 to destroy.",
"@module": "terraform.ui",
"@timestamp": "2021-08-18T22:58:26.461227+08:00",
"changes": {
"add": 2,
"change": 0,
"remove": 0,
"operation": "plan"
},
"type": "change_summary"
}
.tf 与 .tf.json 的格式转换,我们可以用一个小工具 kvz/json2hcl来做转换
# azure/_poc/security_group/provider.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 2.65.0"
}
}
required_version = ">= 1.0.1"
# remote Backend
backend "azurerm" {
resource_group_name = "terraform-30-days-poc"
storage_account_name = "tfstate8b8bff248c5c60c0"
container_name = "tfstate"
key = "_poc/security_group/terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
安装 json2hcl
# azure/_poc/security_group/provider.tf
curl -SsL https://github.com/kvz/json2hcl/releases/download/v0.0.6/json2hcl_v0.0.6_darwin_amd64 \
| sudo tee /usr/local/bin/json2hcl > /dev/null && sudo chmod 755 /usr/local/bin/json2hcl && json2hcl -version
v0.0.6
json2hcl -reverse < azure/_poc/security_group/provider.tf
{
"provider": [
{
"azurerm": [
{
"features": [
{}
]
}
]
}
],
"terraform": [
{
"backend": [
{
"azurerm": [
{
"container_name": "tfstate",
"key": "_poc/security_group/terraform.tfstate",
"resource_group_name": "terraform-30-days-poc",
"storage_account_name": "tfstate8b8bff248c5c60c0"
}
]
}
],
"required_providers": [
{
"azurerm": [
{
"source": "hashicorp/azurerm",
"version": "~\u003e 2.65.0"
}
]
}
],
"required_version": "\u003e= 1.0.1"
}
]
}
然而输入 _poc/security_group/security_group.tf
则会出错
for_each
在 json 中也不存在json2hcl -reverse < azure/_poc/security_group/security_group.tf
unable to parse HCL: At 2:25: Unknown token: 2:25 IDENT local.name
我们可以依据hcl readme 补上 double quote "",让格式符合 json 的格式,这样就可以顺利转换
cat _poc/security_group/security_group_json.tf
resource "azurerm_network_security_rule" "main" {
for_each = "${local.rules}"
name = "${each.value.name}"
priority = "${each.value.priority}"
direction = "${each.value.direction}"
access = "${each.value.access}"
protocol = "${each.value.protocol}"
source_port_range = "${each.value.source_port_range}"
destination_port_range = "${each.value.destination_port_range}"
source_address_prefix = "${each.value.source_address_prefix}"
destination_address_prefix = "${each.value.destination_address_prefix}"
resource_group_name = "${local.resource_group_name}"
network_security_group_name = "${local.name}"
}
json2hcl -reverse < azure/_poc/security_group/security_group_json.tf
{
"resource": [
{
"azurerm_network_security_rule": [
{
"main": [
{
"access": "${each.value.access}",
"destination_address_prefix": "${each.value.destination_address_prefix}",
"destination_port_range": "${each.value.destination_port_range}",
"direction": "${each.value.direction}",
"for_each": "${local.rules}",
"name": "${each.value.name}",
"network_security_group_name": "${local.name}",
"priority": "${each.value.priority}",
"protocol": "${each.value.protocol}",
"resource_group_name": "${local.resource_group_name}",
"source_address_prefix": "${each.value.source_address_prefix}",
"source_port_range": "${each.value.source_port_range}"
}
]
}
]
}
]
}
这也是旧版 terraform 常出现 "${}" 语法的原因。新版 terraform 已经能自动 parse 没有 double quote 的 hcl 语法,并且 validate 时会对有不必要 double quote 的 "${}" 语法跳出 warning。
了解 terraform syntax 後,我们要回头重新检视 hcl 中描述的 terraform resources,以及为什麽 resources 会有这些行为
resource block {}
resource.azurerm_linux_virtual_machine
的 name, size ...等是云端物件的参数for_each
, lifecycle,...)
我们在编写 hcl 内容时应善用 meta-argument,应注意
depends_on
与 variable + argument
的使用,会造成 module 彼此的依赖性
如果对 Terraform 原始码有兴趣,可以看与 hcl config parse
官方文件阅读测验
meta-argument 只在 terraform 内部有效,细节范例我们下一章节会说明
<<: Day13 Lab 2 - Object storage API层
>>: Day 12:「我可不可以 CDN 就好?」- Tailwind 安装及设定
【前言】 其实有蛮多很有趣的的主题,最後会选择做一个平庸的网站,原因是想要圆一个大学时期的後悔 大学...
Logstash Part II 本篇介绍如何搭配filebeat和logstash,把apache...
在HR的EMPLOYEES资料表中,查询2003年6月17日到职的员工姓名及工作部门代码。 SEL...
回顾 30天咻的一下就过去了,第一次的铁人赛暂时画下小句点,那些期待补的篇幅我没忘(?) 漂向何处 ...
当数据庞大时,我们不会把所有资料都存在同一个资料表,会依照资料类型做分类,例如:使用者资料的user...