如何在Terraform中注入包含美元符号($)的Bash脚本?
根因说明
你碰到的是双重插值解析问题:
- 第一层是Terraform解析
metadata_startup_script的heredoc块时,会自动识别${}、$开头的内容做变量/表达式插值 - 第二层是实例启动执行shell脚本时,嵌套的无引号heredoc(
<<EOI)会再次解析块内的$变量、$()命令替换
之前用$$转义得到1397a这类输出,是因为$$被shell解析成了当前启动脚本的进程PID,根本没有按预期完成转义。
最优方案(零修改原始脚本)
完全不需要改动你本地存放的原始Bash脚本,也不需要用File Provisioner、不需要Packer构建自定义镜像,通过base64编码绕开所有插值环节即可:
metadata_startup_script = <<-EOF #!/bin/bash -xe # 读取原脚本base64编码后在实例侧解码写入,全程跳过所有插值解析 echo "${base64encode(file("${path.module}/scripts/simple_bash.sh"))}" | base64 -d | sudo tee /etc/myservice/serv.conf > /dev/null sudo chmod +x /etc/myservice/serv.conf EOF
这个方案的优势:
- 原始脚本文件不需要添加任何转义字符,本地可直接运行调试,写入实例的内容和本地文件完全一致
- 彻底绕开Terraform和shell两层对
$、反引号等特殊字符的解析,不管脚本里有命令替换、变量引用、特殊符号都不会被篡改 - 完全符合GCP实例启动脚本的官方使用规范
备选方案(无编码,零修改原脚本)
如果不希望用base64编码,可以在Terraform配置层统一处理转义,同时给shell的嵌套heredoc标记加单引号禁用shell解析:
metadata_startup_script = <<-EOF #!/bin/bash -xe # EOI加单引号告诉shell不要解析块内内容,Terraform侧统一替换$为$$完成转义 sudo tee /etc/myservice/serv.conf > /dev/null <<'EOI' ${replace(file("${path.module}/scripts/simple_bash.sh"), "$", "$$")} EOI sudo chmod +x /etc/myservice/serv.conf EOF
这里两个核心配置缺一不可:
replace函数把原脚本里所有单个$替换为$$,Terraform解析时会自动将$$还原为单个$,不会触发插值逻辑- 嵌套heredoc的结束标记写为
'EOI'(加单引号),通知bash直接透传块内所有内容,不做任何变量、命令替换解析
之前方案失效的原因
- 直接在嵌套heredoc中嵌入原文件内容:会先后经过Terraform、shell两轮插值,脚本里的
$a、$(curl)要么被Terraform识别为不存在的变量替换为空,要么被shell解析为当前shell的变量 - 手动在原脚本里加
\$、$$:一方面破坏了原脚本的可直接运行性,另一方面没有同时处理两层解析逻辑,要么转义符残留,要么被shell替换为进程PID等非预期内容
内容的提问来源于stack exchange,提问作者aronchick
相关产品推荐
相关产品推荐

