You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby Psych转换YAML时!Ref/!GetAtt被加引号致CloudFormation校验失败

问题根因

这个问题的核心原因是把YAML自定义标签当成了普通字符串传入哈希:Psych序列化时会检测字符串里的YAML特殊字符,!作为自定义标签的起始标记属于特殊字符,为了保证序列化结果反序列化时语义不变,Psych会自动给这类字符串包裹双引号做转义,根本不会把它解析成自定义标签节点。同理${AWS::AccountId}被加引号,也是因为Psych默认对含特殊语法特征的字符串做安全转义,避免解析歧义。

解决方案

不要把CloudFormation的自定义标签写成普通字符串,通过Psych提供的自定义标量能力声明标签节点即可,有两种稳定实现方式:

方式1:快速构造自定义标量(仅生成场景适用)

不需要额外封装复杂逻辑,写个快捷方法构造带标签的标量节点,直接替换哈希里的字符串值就行:

require 'psych'

# 构造CloudFormation自定义标签值
def cfn_tag(tag_name, value)
  Psych::Nodes::Scalar.new(
    value,
    nil,
    nil,
    false,
    false,
    Psych::Nodes::Scalar::PLAIN # 强制纯文本格式输出,不自动加引号
  ).tap { |node| node.tag = "!#{tag_name}" }
end

template = {
  'FooPolicy' => {
    'Type' => 'AWS::SQS::QueuePolicy',
    'Properties' => {
      'Queues' => cfn_tag('Ref', 'FooQueue'),
      'PolicyDocument' => {
        'Statement' => {
          'Action' => 'SQS:*',
          'Effect' => 'Allow',
          'Resource' => cfn_tag('GetAtt', 'FooQueue.Arn'),
          'Principal' => {
            'AWS' => '${AWS::AccountId}'
          }
        }
      }
    }
  }
}

# 序列化时关闭自动折行转义,去掉默认YAML文档头
output = Psych.dump(
  template,
  indentation: 2,
  line_width: -1
).gsub(/^---\n/, '')

puts output

运行后输出完全符合CloudFormation格式要求:

FooPolicy:
  Type: AWS::SQS::QueuePolicy
  Properties:
    Queues: !Ref FooQueue
    PolicyDocument:
      Statement:
        Action: SQS:*
        Effect: Allow
        Resource: !GetAtt FooQueue.Arn
        Principal:
          AWS: ${AWS::AccountId}

方式2:注册自定义标签类(序列化/反序列化双向兼容)

如果场景需要同时支持解析现有CloudFormation模板、生成新模板,可以注册专属标签类,适配Psych的编解码钩子:

class CfnTag
  attr_accessor :tag, :value
  def initialize(tag, value)
    @tag = tag
    @value = value
  end

  # 序列化逻辑
  def encode_with(coder)
    coder.tag = "!#{@tag}"
    coder.scalar = @value
    coder.style = Psych::Nodes::Scalar::PLAIN
  end

  # 反序列化逻辑
  def init_with(coder)
    @tag = coder.tag.sub(/^!/, '')
    @value = coder.scalar
  end
end

# 注册常用CloudFormation标签
%w[Ref GetAtt Sub ImportValue Select Split].each do |tag|
  Psych.add_tag("!#{tag}", CfnTag)
end

后续构造哈希时直接传入CfnTag.new('Ref', 'FooQueue')这类实例即可,双向转换都不会出现多余引号或标签识别错误。

注意事项
  • 不要用正则全局替换输出结果里的双引号,会导致本身包含冒号、空格、换行等特殊字符、确实需要引号包裹的值出现格式错误。
  • 遇到普通伪参数(比如${AWS::Region}、${AWS::AccountId})被自动加引号的情况,给Psych.dump传入line_width: -1参数关闭自动折行和过度转义即可解决。

内容的提问来源于stack exchange,提问作者Bao Thai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 16:09:20