这是我的 Bash 脚本:
#!/bin/bash
#Colors for terminal
#RED
R="\e[31m"
#GREEN
G="\e[32m"
#YELLOW
Y="\e[33m"
#NORMAL
N="\e[0m"
validation(){
if [ $1 -eq 0 ];
then
echo -e "${G}$2 is successful${N}"
else
echo -e "${R}$2 has Failed${N}"
exit 1;
fi
}
echo -e "${Y}Configuring Cart Service${N}"
cart_config="/etc/systemd/system/cart.service"
cat << EOF >$cart_config
Environment=REDIS_HOST="${redis_ip}" #I want TF to only substitute these
Environment=CATALOGUE_HOST="${app_lb_dns}"
Environment=CATALOGUE_PORT=8082
EOF
validation $? "Configuring Cart Service"
Terraform 创建 Redis-Ip 地址和负载均衡器 DNS 后,我会收到它们,我想将替换这些值的 Bash 脚本作为用户数据传递。问题是 Terraform 试图替换${}
Bash 脚本中的每个变量,但我只想将${redis_ip}
和替换${app_lb_dns}
为用户数据。我尝试使用\${}
和转义所有变量,$${}
但没用。
错误:
20:user_data = base64encode(templatefile(“../userdata/cart.sh”,{redis_ip = data.aws_ssm_parameter.redis_ip.value,app_lb_dns = data.aws_ssm_parameter.app_lb_dns.value}))在调用templatefile(path,vars)时“vars”参数的值无效:vars映射不包含键“G”,在../userdata/cart.sh:16,21-22处引用。
根据错误,TF 正在尝试替换与 bash 脚本相关的变量。
这是我的 Terraform 代码:
user_data = base64encode(templatefile("../userdata/cart.sh", { redis_ip = data.aws_ssm_parameter.redis_ip.value, app_lb_dns = data.aws_ssm_parameter.app_lb_dns.value }))
为了让 terraform 忽略任何 bash 变量,您必须
$
在每个 前面添加一个额外的$
。看一下 terraform转义序列。换句话说,您必须将模板文件修复为如下所示:
因为 Bash 和 Terraform 都使用
${ ... }
标点符号来表示插值,所以如果您使用 Terraform 的模板语法来生成 Bash 脚本,那么在任何插值序列应该由 Bash 而不是 Terraform 解释的情况下,您都需要使用 Terraform 的转义语法。例如:
请注意,Terraform 的模板语言仅将 视为
${
特殊,而 Bash 同时支持$foo
和${foo}
。您只需对特定的序列进行转义${
;无需对后面没有左括号字符的单个美元符号进行转义。此后的一切都是在探索一些解决问题的不同方法,这些方法有一些其他的权衡,但如果您对逃避一切感到满意,那么很好,您可以停止阅读。
您可以通过重新排列脚本使其仅包含一小部分模板部分并且大部分只是静态文件来避免这种冲突。
例如,如果您将大部分脚本放在名为的静态 bash 脚本文件中
cart.sh
,那么您可以在表达式中单独生成小的动态部分user_data
,同时完全避免将其视为cart.sh
Terraform 模板:请注意,这使用
file
而不是templatefile
来读取cart.sh
文件,因此 Terraform 将完全按照字面意思理解内容,而不是尝试将其解释为模板。中的代码
cart.sh
可以编写为引用变量cart_config
,该变量在整体结果中使用模板化命令声明declare
。由于这是 Terraform 模块中唯一需要包含来自其他位置的动态数据的变量,因此无需在cart.sh
文件中进行额外的转义。上述基于内联模板的解决方案应该可以正常工作,但我个人发现它的形状难以维护,因此我编写了一个实用程序提供程序,
apparentlymart/bash
它知道如何生成合适的declare
语句并将它们插入到 Bash 脚本的开头。可以使用我的实用程序提供程序重写前面的示例,如下所示:
本例中的调用
provider::bash::script
应产生与我上一个示例中手写模板类似的结果。这个提供者贡献的函数还可以支持字符串映射并将它们声明为 Bash“关联数组”,这允许另一种选择,即可以在 bash 脚本中处理“cart config”文件语法的细节,而不是在 Terraform 配置中处理:
上述意味着将会有一个名为的 Bash 变量,
environment
其值是一个包含三个给定元素的关联数组,然后您可以在 Bash 脚本内部对其进行迭代:对于这种简单的情况,这种额外的复杂性是否值得仍有争议,但当脚本中使用的映射的键是动态选择时,这种技术会很方便,因为脚本只会对给定的任何键/值对做出反应,而不是期望固定的集合。
(如果变量名或变量值可能包含 Bash 会非字面解释的字符,例如“通配符”,则上述技术会比较棘手。在我的提供商文档中的“其他 Bash 健壮性技巧”中有一些关于此方面的想法。由于这是使用 Bash 生成另一种语言——systemd 单元文件语言——您还需要确保键和值字符串不包含任何从 systemd 角度来看会使其无效的内容。)