在不修改远程tfstate的情况下,安全地确认terraform state mv后的计划差异的步骤是什么?
首先
在长期运营Terraform时,有时候我们希望重命名资源名称,以进行一些重构工作。
对于这种情况,我们可以使用terraform state mv命令来实现,该命令默认会直接修改tfstate(Terraform的状态管理文件)。
更多信息请参考:https://www.terraform.io/docs/commands/state/mv.html
然而,在团队开发中,*.tf文件通常通过Git进行版本管理,而tfstate文件则被保存在远程的后端(例如AWS S3)。换句话说,如果直接修改远程的tfstate文件,会导致与主分支的状态产生差异。
在对tf文件进行更改审查之前,我们希望确认在未修改远程tfstate的情况下,执行terraform state mv后,计划的差异将消失。根据初步的搜索结果,只找到了一些关于terraform state mv的简单例子,没有找到结合远程tfstate的安全正确的步骤,因此我将分享已建立的步骤。
环境
我已经使用最新版本的Terraform和AWS提供商进行了操作确认。
-
- terraform v0.12.29
- terraform-provider-aws v3.0.0
因为除了AWS之外的云提供商基本上都具有相同的操作步骤,所以可以根据情况进行相应的调整。
预备状态
初始状态
为了说明,我准备了一个如下所示的tf文件。
terraform {
required_version = "0.12.29"
backend "s3" {
region = "ap-northeast-1"
bucket = "yourbacket"
key = "test/terraform.tfstate"
}
}
provider "aws" {
version = "3.0.0"
region = "ap-northeast-1"
}
resource "aws_security_group" "foo" {}
resource "aws_security_group" "bar" {}
假设已经预先执行了 “terraform init && terraform apply”,并且达到了以下所示的状态。
$ terraform state list
aws_security_group.bar
aws_security_group.foo
更改资源名称
请假设我们为了说明,在tf文件中将”foo”重命名为”foo1″。
$ git diff
diff --git a/main.tf b/main.tf
index 0819129..e504c28 100644
--- a/main.tf
+++ b/main.tf
@@ -13,5 +13,5 @@ provider "aws" {
region = "ap-northeast-1"
}
-resource "aws_security_group" "foo" {}
+resource "aws_security_group" "foo1" {}
resource "aws_security_group" "bar" {}
当然,计划的结果会有所不同。
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
aws_security_group.bar: Refreshing state... [id=sg-0339c0b83295dd56a]
aws_security_group.foo: Refreshing state... [id=sg-0465466ea3f389dde]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
# aws_security_group.foo will be destroyed
- resource "aws_security_group" "foo" {
(snip.)
}
# aws_security_group.foo1 will be created
+ resource "aws_security_group" "foo1" {
(snip.)
}
Plan: 1 to add, 0 to change, 1 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
Please clarify what you mean by “state mv手順.” Are you referring to explaining the steps or procedure for using the “mv” command in a terminal or a specific motion or action related to “mv”?
接下来,进入正题。
表述国家撤退
首先从远程拉取最新的tfstate,并暂时保存在本地。
$ terraform state pull > tmp.tfstate
将后端切换至本地。
接下来,将暂时将后端切换到本地。
尽管可以直接在编辑器中编辑.tf文件中的后端,但这样稍显繁琐。因此,我们将生成一个override文件来覆盖定义后端的内容。如果override.tf在其他地方已经存在,请使用不会冲突的名称,如_hoge_override.tf。
https://www.terraform.io/docs/configuration/override.html
对于这些罕见的情况,Terraform对以_override.tf或_override.tf.json结尾的任何配置文件有特殊处理。这种特殊处理同样适用于名为override.tf或override.tf.json的文件。
$ cat << EOF > override.tf
terraform {
backend "local" {
}
}
EOF
如果更改了后端的定义,就需要重新进行初始化。
$ terraform init -reconfigure
Initializing the backend...
Successfully configured the backend "local"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
成功配置了后端”local”!Terraform将自动
可以确认已经切换到本地。
陈述音乐视频
所以,请执行所需的状态变更。
在这种情况下,请不要忘记使用 -state 标志指定本地的 tfstate 文件。
$ terraform state mv -state=tmp.tfstate aws_security_group.foo aws_security_group.foo1
Move "aws_security_group.foo" to "aws_security_group.foo1"
Successfully moved 1 object(s).
计划 (jì huà)
如果能够成功执行 mv 命令,我将确认计划中没有差异。
我也会在 plan 命令中使用 -state 标志来传递本地的 tfstate 文件。
$ terraform plan -state=tmp.tfstate -detailed-exitcode
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
aws_security_group.bar: Refreshing state... [id=sg-0339c0b83295dd56a]
aws_security_group.foo1: Refreshing state... [id=sg-0465466ea3f389dde]
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
$ echo $?
0
由于计划中存在一个“state”标志,所以一开始看起来,即使后端是远程的,似乎也可以指定本地文件。然而,需要注意的是,如果后端是远程的,该选项将被忽略。
– 状态=路径 – 状态文件的路径。默认为”terraform.tfstate”。当使用远程状态时忽略。
评论
可以创建一个适当的分支来更改tf文件,并提交更改,然后向团队内成员请求Pull Request进行审查。在此过程中,最好将用于验证的state mv命令附在Pull Request上作为参考。
如果在等待审核期间不小心保持本地状态并进行其他操作,可能会出现意外情况,因此我们将后端切换回远程状态。
$ rm override.tf
$ terraform init -reconfigure
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
成功配置了后端”S3″!Terraform将自动进行配置
可以确认已切换到遥控操作。
完成评论后,将其合并到主分支并进行 git pull。
远程传输的反映
在等待审查的期间,由于有可能有人进行更改,因此我会再次在主分支上按照相同步骤将后台切换到本地,并确认计划没有差异。
$ terraform state pull > tmp.tfstate
$ cat << EOF > override.tf
terraform {
backend "local" {
}
}
EOF
$ terraform init -reconfigure
$ terraform state mv -state=tmp.tfstate aws_security_group.foo aws_security_group.foo1
$ terraform plan -state=tmp.tfstate -detailed-exitcode
$ echo $?
如果没有问题的话,将后端远程返回。
$ rm override.tf
$ terraform init -reconfigure
将state推送到远程。
$ terraform state push tmp.tfstate
如果使用 CI/CD 进行自动应用,请注意避免与状态推送时间冲突。由于无法控制自动应用的批准时间,可能会在合并到主分支之前无法修改状态而导致自动应用失败。
确认远程状态已经被反映。
$ terraform state list
aws_security_group.bar
aws_security_group.foo1
出于预防的目的,我会确认计划没有差异。
$ terraform plan -detailed-exitcode
(snip.)
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
$ echo $?
0
我会清除临时的本地tfstate垃圾。
$ rm tmp.tfstate*
结束了。
补充说明
由于这个过程非常复杂且麻烦,我写了一个名为tfmigrate的工具来完全自动化这些步骤↓
为了将Terraform的状态操作提交到git,我写了一个名为tfmigrate的工具。