如何使Terraform模块中的Provisioner正常运行?
我完全明白你遇到的困扰——把provisioner放在GCE实例模块里时完全没动静,移到根目录的main.tf就正常运行,这确实让人头疼。不用把所有代码都塞进一个庞大的main.tf里,我们来一步步解决这个问题:
核心原因分析
你遇到的情况大概率和Terraform对模块内资源的执行逻辑有关:当模块内的资源没有被根模块显式依赖(比如引用输出值),或者使用了较旧版本的Terraform时,provisioner可能会被静默跳过,哪怕资源本身创建成功了。另外你的local-exec也不执行,说明问题不是SSH连接配置导致的,而是provisioner根本没被触发。
具体解决办法
1. 升级Terraform到最新稳定版本
旧版本的Terraform(比如0.11及更早)在处理模块内的provisioner时存在兼容性bug,升级到1.x系列的稳定版本(目前推荐1.5+)可以解决大部分这类基础逻辑问题。
2. 在模块中添加输出并在根模块引用
让根模块明确依赖模块内的资源,强迫Terraform等待模块内所有操作(包括provisioner)完成:
- 在
./gce/main.tf末尾添加输出:
output "instance_id" { value = google_compute_instance.gce-instance.id }
- 在根目录
main.tf中引用这个输出(哪怕只是输出它):
module "instance1" { source = "./gce" name = "micro1" machine_type = "f1-micro" boot_image = "debian-cloud/debian-9" zone = "europe-west1-b" ip_address = "10.132.0.31" } output "instance1_id" { value = module.instance1.instance_id }
这样Terraform会确保模块内的provisioner被执行,因为根模块需要等待模块输出的实例ID生成。
3. 修正file provisioner的连接配置(针对SSH场景)
虽然你的local-exec也有问题,但如果后续要让file provisioner正常工作,还要注意几个关键点:
- SSH用户名:Debian 9的默认用户名是
debian,不是你配置的someUser,这是最常见的错误之一 - 私钥权限:确保你的私钥文件权限是
600(执行chmod 600 /somePathToKey/id_rsa),否则SSH会拒绝使用 - 防火墙规则:确认GCE实例所在的网络允许SSH访问(默认的default网络通常已经开放,但最好检查一下)
4. 测试local-exec的小技巧
为了确认local-exec是否执行,可以修改命令为绝对路径,方便检查结果:
provisioner "local-exec" { command = "echo test >> /tmp/terraform_module_test.txt" }
执行terraform apply后,查看/tmp/terraform_module_test.txt是否存在并包含内容,就能快速判断provisioner是否运行。
额外提示
如果后续需要重新执行provisioner(比如修改了配置文件),可以用terraform taint命令标记模块内的实例为"脏",这样Terraform会重新创建实例并执行provisioner:
terraform taint module.instance1.google_compute_instance.gce-instance
内容的提问来源于stack exchange,提问作者Tomasz

