Terraform State 之你的 Local State 不是我的 State
State 是初学 Terraform 的核心概念,本章节会讲解基本的 State 原理。
课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days
赛後文章会整理放到个人的部落格上 http://chechia.net/
上一讲 Day 02-是在 Hello?什麽都要 Hello 一下之 Hello Terraform,我们操作 terraform 指令,来 create / update / destroy 远端的资源。在执行完成 terraform apply 後,本地资料夹会产生一个 terraform.tfstate 档案。
首先,我们看一下 terraform.tfstate 档案的内容。你可以使用文字编辑器,或是透过 shell 与 jq 工具来检视 State
cat terraform.tfstate
cat terraform.tfstate | jq keys
[
"lineage",
"outputs",
"resources",
"serial",
"terraform_version",
"version"
]
其中 .resources 是纪录 .tf 档案产生的资源,在远端的 instance 的实际资料。换句话说,Terraform apply 後产生资源满足 .tf 的描述,而实际在远端的实体是哪一个,有哪些资料,纪录在 State 中。
再举个例,如果我们想要产生多个不同的 _poc/foundation/*.tf
中的 resource,复制 .tf 档案在 apply,会获得另一组 foundation resource,如另一组 resource group 与 storage account,有相同的参数,但有不同的远端 id。
我们还可以比较 .tfstate 的内容,与 azure console 上看到的内容,更能理解两者个关系。
根据官方文件 描述 State 的设计与功能,这边简述几个重点,底下会有范例详述
厘清名词 State
使用 Local State 有许多好处
然而使用 Local State 也有以下几个问题
Q: 没有 .tfstate 档案就无法使用 Terraform 吗?可是我的 .tf 档案里 name 写得很清楚,这样 Terraform 抓不到远端相同名字的资源吗?
我们可以做个实验,切换到另外一个 foundation cloned 资料夹,内容与 foundation 完全一致。
cd ../foundation_cloned
terraform init && terraform plan
plan 的结果是什麽?
是否令人有些疑惑?
我们原先预想透过相同的 .tf 档案来管理 resourceGroup/terraform-30-days-poc,然而 Terraform 无法追踪远端已经存在的 resource group,而是选择 create 一个新的 resource group,provider API 送出後,远端的 Azure 回传错误
更明确的说明:没有 State 便无法 mapping .tf resource,与远端 resource
我们可以进一步检视,.tfstate 档案的哪些内容,使得一边可以正常使用 Terraform,一边何谓产生错误。使用 jq 工具来检视 State 中与 resource gropu 有关的资料。
cat terraform.tfstate | jq '.resources[0]'
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "rg",
"provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "/subscriptions/6fce7237-7e8e-4053-8e7d-ecf8a7c392ce/resourceGroups/terraform-30-days-poc",
"location": "southeastasia",
"name": "terraform-30-days-poc",
"tags": null,
"timeouts": null
},
"sensitive_attributes": [],
"private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo1NDAwMDAwMDAwMDAwLCJkZWxldGUiOjU0MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjo1NDAwMDAwMDAwMDAwfX0="
}
]
}
cat terraform.tfstate | jq '.resources[0].instances[0].attributes'
{
"id": "/subscriptions/6fce7237-7e8e-4053-8e7d-ecf8a7c392ce/resourceGroups/terraform-30-days-poc",
"location": "southeastasia",
"name": "terraform-30-days-poc",
"tags": {},
"timeouts": null
}
Terraform 产生 resource group 後,将 azure API 回覆的 resources 各项参数纪录在 .tfstate 中。下次要再进行编辑时,我们编辑 .tf 档案,Terraform 则会依据 .tfstate 档案去 mapping 远端的 resource group,进行 plan 与 apply。
熟悉 RESTful API 的朋友可以这样想:使用 POST API 产生物件时,後端 server 会回传 id 在 response body 中,下次要编辑这个相同物件,则需要使用 id 作为辨识。Terraform 其实是相同的原理,只是在这个例子中,Terraform 协助托管了 id 这个 metadata。
既然使用 terraform 时,必须仰赖 State .tf 档案,那是否一起协作的团队成员就必须要取得 .tfstate 档案,才能正确的操作 Terraform?
是的,这也是使用 Local Backend 的 .tfstate 档案,最大的问题,会造成多人协作十分困难。过往旧版的 Terraform 有几个妥协的做法:
将 .tfstate 档案与 .tf 档案一起纳入版本控制系统,具体流程可能是这样
理想上是这样,但实务上还是非常不便
本课程不建议把 .tfstate 加入到版本控制,本 repository 已经将.tfstate 加入到 .gitignore 中。
所有在 terraform plan / apply 中产生的 data 与 meta-data,都会纪录在 tfstate 中,那是否有一些 State 内容是敏感资料,不希望让他人看到?
我们可以使用 _poc/user/
为例子。首先检视一下内容 .tf 档案,这个档案使用 Terraform random password 来产生一组随机密码,然後使用这组密码作为新的 User 的登入密码。
# _poc/user/ad_user.tf
resource "random_password" "terraform" {
length = 16
special = true
override_special = "_%@"
}
resource "azuread_user" "terraform" {
user_principal_name = "[email protected]" # Need valified domain on Azure AD
display_name = "Terraform Runner"
mail_nickname = "terraform"
password = random_password.terraform.result
}
产生 user terraform 资源
terraform apply
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ password = (sensitive value)
+ user_principal_name = "[email protected]"
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
password = <sensitive>
user_principal_name = "[email protected]"
Apply 完成後,我们可以看一下 _poc/user
产生的资料,这边有点有趣
我们可以在用其他方法印出 sensitive 资料:
terraform output
password = <sensitive>
user_principal_name = "[email protected]"
terraform output -json
{
"password": {
"sensitive": true,
"type": "string",
"value": "QfgdxMwVRKr41nHG"
},
"user_principal_name": {
"sensitive": false,
"type": "string",
"value": "[email protected]"
}
}
cat terraform.tfstate | jq '.resources[1].instances[0]'
{
"schema_version": 0,
"attributes": {
"id": "none",
"keepers": null,
"length": 16,
"lower": true,
"min_lower": 0,
"min_numeric": 0,
"min_special": 0,
"min_upper": 0,
"number": true,
"override_special": "_%@",
"result": "QfgdxMwVRKr41nHG",
"special": true,
"upper": true
},
"sensitive_attributes": [],
"private": "bnVsbA=="
}
我们可以看到,所有密码还是明码的印出来。
更好的方式是
下堂,我们要来使用远端的 Backend 与 State
尝试使用 terraform state 的几个指令
terraform state --help
terraform state list
terraform state show
terraform destroy 删除所有本课程的产物
阅读以下官方文件
<<: 30天轻松学会unity自制游戏-了解unity基础操作
USART介绍 USART全名为通用同步/非同步收发传输器(universal synchronou...
在正文之前要说一下, 其实我觉得在留言板用文字编辑器不是个好主意, 反而应该放在心情随笔的地方, (...
前情提要 被第一人视角的我打断了对话,现在要继续讲完: 在D22的时候,我们知道了识别字、保留字,其...
昨天我们把一资料库建里好之後,今天我们学习怎麽把资料写进去吧~ 在MainVC里面先建立一个空阵列,...
Youtube 频道:https://www.youtube.com/c/kaochenlong ...