Terraform销毁模块前执行作业禁用SSH密钥方案异常问题
问题根因
你当前方案触发无限循环的核心问题是犯了Terraform的典型反模式:在provisioner执行流程中嵌套调用Terraform命令操作同一套配置的资源。
- 外层
terraform destroy/模块置零触发的销毁流程,在启动时就已经生成了固定的执行计划、持有状态操作上下文,你在provisioner里启动的内层terraform apply会读写同一份state文件,两个进程互相感知对方的资源变更,直接进入循环触发的死锁状态。 - 加上你加了
-lock=false参数关闭了状态锁,两个进程同时写state本质上是在制造状态损坏的风险,就算不循环也很容易把state搞乱。 - HashiCorp官方明确不推荐在provisioner中调用terraform命令操作当前管理的资源,这种做法从设计上就不可行。
正确实现思路
你不需要嵌套调用terraform,顺着Terraform的生命周期依赖规则实现即可,核心逻辑是:让清理SSH密钥的动作成为模块销毁时第一个执行的步骤,等清理完成后再走后续资源销毁流程。
具体实现步骤如下:
- 删掉原有null_resource里嵌套的terraform apply命令,这是导致循环的直接原因。
- 不要通过Terraform apply触发清理job,直接在destroy provisioner中调用kubectl提交清理job并等待执行完成,避免跨Terraform进程操作。
- 调整资源依赖顺序,确保承载清理逻辑的null_resource是模块中最后创建的资源——Terraform销毁资源的顺序和创建顺序完全相反,最后创建的资源会第一个被销毁,刚好满足「先清理、后删其他资源」的要求。
参考配置示例:
# 读取SSH密钥清理job的yaml模板,模板里写死密钥移除的逻辑即可 data "template_file" "ssh_cleanup_job" { template = file("${path.module}/manifests/ssh_key_cleanup_job.yaml") } resource "null_resource" "cleanup_ssh_before_destroy" { # 显式依赖模块内所有需要在清理完成后再销毁的资源 # 包括你原来的ssh_toggle_job、EKS节点组、集群相关资源 depends_on = [ kubernetes_job.ssh_toggle_job, # 补充其他需要后置销毁的资源 ] provisioner "local-exec" { when = destroy interpreter = ["/bin/bash", "-c"] command = <<-EOT # 提交清理job到EKS集群 echo '${data.template_file.ssh_cleanup_job.rendered}' | kubectl apply -f - # 等待job执行完成,超时时间可根据实际节点规模调整 kubectl wait -n kube-system job/ssh-key-cleanup --for=condition=complete --timeout=300s # 清理临时job资源,避免集群残留 echo '${data.template_file.ssh_cleanup_job.rendered}' | kubectl delete -f - --wait=false EOT } }
注意事项
- 执行Terraform命令的运行环境必须提前配置好EKS集群的访问权限,确保kubectl可以正常连接集群,否则provisioner会执行失败卡住销毁流程。
- 如果你是通过将模块count设为0的方式触发模块禁用销毁,需要保证这个null_resource的count逻辑和模块完全一致,且depends_on覆盖了模块内所有相关资源,避免Terraform提前销毁集群访问权限相关的资源导致清理job无法提交。
- 不要给这个null_resource配置
create_before_destroy生命周期规则,否则会导致清理动作在资源重建时提前触发,不符合预期。
内容的提问来源于stack exchange,提问作者Theo Sweeny
相关产品推荐
相关产品推荐

