AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79595799
Accepted
N-CODER
N-CODER
Asked: 2025-04-28 12:27:31 +0800 CST2025-04-28 12:27:31 +0800 CST 2025-04-28 12:27:31 +0800 CST

如何使 terraform 模板文件忽略默认的 bash 变量?

  • 772

这是我的 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
  • 2 2 个回答
  • 47 Views

2 个回答

  • Voted
  1. Marko E
    2025-04-28T18:26:09+08:002025-04-28T18:26:09+08:00

    为了让 terraform 忽略任何 bash 变量,您必须$在每个 前面添加一个额外的$。看一下 terraform转义序列。

    换句话说,您必须将模板文件修复为如下所示:

    #!/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"
    
    • 1
  2. Best Answer
    Martin Atkins
    2025-04-29T00:58:03+08:002025-04-29T00:58:03+08:00

    因为 Bash 和 Terraform 都使用${ ... }标点符号来表示插值,所以如果您使用 Terraform 的模板语法来生成 Bash 脚本,那么在任何插值序列应该由 Bash 而不是 Terraform 解释的情况下,您都需要使用 Terraform 的转义语法。

    例如:

    echo -e "$${Y}Configuring Cart Service$${N}"
    

    请注意,Terraform 的模板语言仅将 视为${特殊,而 Bash 同时支持$foo和${foo}。您只需对特定的序列进行转义${;无需对后面没有左括号字符的单个美元符号进行转义。

    此后的一切都是在探索一些解决问题的不同方法,这些方法有一些其他的权衡,但如果您对逃避一切感到满意,那么很好,您可以停止阅读。


    您可以通过重新排列脚本使其仅包含一小部分模板部分并且大部分只是静态文件来避免这种冲突。

    例如,如果您将大部分脚本放在名为的静态 bash 脚本文件中cart.sh,那么您可以在表达式中单独生成小的动态部分user_data,同时完全避免将其视为cart.shTerraform 模板:

      user_data = <<-EOT
        #!/bin/bash
    
        declare -r cart_config=<<EOF
        Environment=REDIS_HOST="${data.aws_ssm_parameter.redis_ip.value}"
        Environment=CATALOGUE_HOST="${data.aws_ssm_parameter.app_lb_dns.value}"
        Environment=CATALOGUE_PORT=8082
        EOF
    
        ${file("${path.module}/../userdata/cart.sh")}
      EOT
    

    请注意,这使用file而不是templatefile来读取cart.sh文件,因此 Terraform 将完全按照字面意思理解内容,而不是尝试将其解释为模板。

    中的代码cart.sh可以编写为引用变量cart_config,该变量在整体结果中使用模板化命令声明declare。由于这是 Terraform 模块中唯一需要包含来自其他位置的动态数据的变量,因此无需在cart.sh文件中进行额外的转义。

    echo >/etc/systemd/system/cart.service "$cart_config"
    

    上述基于内联模板的解决方案应该可以正常工作,但我个人发现它的形状难以维护,因此我编写了一个实用程序提供程序,apparentlymart/bash它知道如何生成合适的declare语句并将它们插入到 Bash 脚本的开头。

    可以使用我的实用程序提供程序重写前面的示例,如下所示:

    # Terraform requires that your module must declare a
    # dependency on this provider in order for its functions
    # to be available for use elsewhere in the module.
    terraform {
      required_providers {
        bash = {
          source = "apparentlymart/bash"
        }
      }
    }
    
    resource "aws_instance" "example" {
      # ...
      user_data = provider::bash::script(file("${path.module}/../userdata/cart.sh"), {
        cart_config = <<-EOT
          Environment=REDIS_HOST="${data.aws_ssm_parameter.redis_ip.value}"
          Environment=CATALOGUE_HOST="${data.aws_ssm_parameter.app_lb_dns.value}"
          Environment=CATALOGUE_PORT=8082
        EOT
      })
    }
    

    本例中的调用provider::bash::script应产生与我上一个示例中手写模板类似的结果。


    这个提供者贡献的函数还可以支持字符串映射并将它们声明为 Bash“关联数组”,这允许另一种选择,即可以在 bash 脚本中处理“cart config”文件语法的细节,而不是在 Terraform 配置中处理:

    # (...again, a required_providers block must appear in your module...)
    
    resource "aws_instance" "example" {
      # ...
      user_data = provider::bash::script(file("${path.module}/../userdata/cart.sh"), {
        environment = tomap({
          REDIS_HOST     = data.aws_ssm_parameter.redis_ip.value
          CATALOGUE_HOST = data.aws_ssm_parameter.app_lb_dns.value
          CATALOGUE_PORT = "8082"
        })
      })
    }
    

    上述意味着将会有一个名为的 Bash 变量,environment其值是一个包含三个给定元素的关联数组,然后您可以在 Bash 脚本内部对其进行迭代:

    cart_config="/etc/systemd/system/cart.service"
    # Create the file, or truncate it if it already exists
    > $cart_config
    # Append each pair from "environment" as a line in the file
    for k in "${!environment[@]}"; do
        echo >>$cart_config "Environment=${k}=\"${environment["$k"]}\""
    done
    

    对于这种简单的情况,这种额外的复杂性是否值得仍有争议,但当脚本中使用的映射的键是动态选择时,这种技术会很方便,因为脚本只会对给定的任何键/值对做出反应,而不是期望固定的集合。

    (如果变量名或变量值可能包含 Bash 会非字面解释的字符,例如“通配符”,则上述技术会比较棘手。在我的提供商文档中的“其他 Bash 健壮性技巧”中有一些关于此方面的想法。由于这是使用 Bash 生成另一种语言——systemd 单元文件语言——您还需要确保键和值字符串不包含任何从 systemd 角度来看会使其无效的内容。)

    • 0

相关问题

  • 在 Terraform 中动态生成 JSON

  • 如果 Terraform 不存在,则创建用户

  • Terraform:从列表指定的地图中复制特定元素

  • Terraform 模板不会按值替换变量

  • 无法从 ALB 模块的多个 ID 中检索单个 EC2 实例 ID

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve