在无服务器 yaml 配置中使用 fn::split 不起作用

问题描述

我正在使用无服务器框架在 AWS 上部署 API。我的 serverless.yml 文件中有以下内容

custom:
  vpcSettings:
    private:
      securityGroupIds:
        private:
          fn::split:
            delimiter: ','
            value: ${env:VPC_SG_ID}

VPC_SG_ID 包含以下字符串:sg-1111111111,sg-222222222,sg-3333333333

但是,在部署应用程序时,出现以下错误An error occurred: MyLambdaFunction - Value of property SecurityGroupIds must be of type List of String.

如果我对 SG 列表进行硬编码,则它可以正常工作:

custom:
  vpcSettings:
    private:
      securityGroupIds:
        private:
          - "sg-1111111111"
          - "sg-2222222222"
          - "sg-3333333333"

为什么 fn::split 函数不返回字符串列表?

编辑:

以下配置导致同样的错误

custom:
  vpcSettings:
    private:
      securityGroupIds:
        private:
          Fn::Split:
            - ','
            - ${env:VPC_SG_ID}

解决方法

如果安全组作为输入参数添加到模板

Parameters:
  VPCSGID:
    Type: String
    Description: Comma separated Security Groups

安全组可以用 !Split as 分割

SecurityGroupIds: !Split [",",!Ref VPCSGID]

可以用 Fn:Split as 分割

SecurityGroupIds: { "Fn::Split": [",!Ref VPCSGID] }

sam deploy 的参数可以作为

sam deploy --parameter-overrides 'ParameterKey=VPCSGID,ParameterValue=sg-011111,sg-222222'
,

我正在尝试实现相同的目标,但我很确定您不能在 Fn::Split 中使用 CloudFormation 函数 (serverless.yml)。

替代方案 1

通过 env vars 传递 YAML 列表/数组对于无服务器似乎是不可能的,因为我们无法编写代码来解析来自 env vars 的字符串。也许你可以写一个无服务器插件,但我还没有研究过。

我假设您需要能够传递可变长度和任意值的列表,因此以下解决方案可能不适合您。

如果您有已知值,您可以将列表保存在 serverless.yml 中并选择一个带有环境变量的列表:

custom:
  sgLists:
    list1:
      - sg-11
      - sg-22
    list2:
      - sg-33
      - sg-44
  vpcSettings:
    private:
      securityGroupIds:
        private: ${self:custom.sgLists.${env:LIST_SELECTOR}}

...然后您选择一个列表:

LIST_SELECTOR=list1 sls print
LIST_SELECTOR=list2 sls print

如果您需要任意值,并且您的列表不会太长,那么您可能会变得非常笨拙,并将每个列表项设为 env var:

custom:
  sgLists:
    twoItems:
      - ${env:SG1}
      - ${env:SG2}
    threeItems:
      - ${env:SG1}
      - ${env:SG2}
      - ${env:SG3}
  vpcSettings:
    private:
      securityGroupIds:
        private: ${self:custom.sgLists.${env:LIST_SELECTOR}}

..并提供所有内容作为环境变量:

LIST_SELECTOR=twoItems SG1=sg-11 SG2=sg-22 sls print
LIST_SELECTOR=threeItems SG1=sg-11 SG2=sg-22 SG3-33 sls print

替代方案 2

您还可以利用无服务器功能reference properties in other files。您可以创建一个小的 YAML 文件(从版本控制中忽略):

cd `dirname "$0"`
cat <<EOF > ../sgConfig.yml
groups:
  - sg-11
  - sg-22
EOF

...然后在您的 serverless.yml 中引用该文件:

custom:
  vpcSettings:
    private:
      securityGroupIds:
        private: ${file(./sgConfig.yml):groups}

解释错误信息

我认为您的错误消息是指您在需要 YAML 列表/数组时将 YAML 对象/字典传递到 private 中。关键是这与 YAML 语法有关。 Fn::Split 函数是 specific to AWS CloudFormation 函数,我猜这个函数的“执行”发生在 AWS 基础设施中。

我们可以通过更改您的代码来提供一个列表来测试这个理论:

        private:
          - fn::split: # add the "-" for a list
            delimiter: ','

...然后你会看到一个类似

的错误
Configuration error at 'custom.vpcSettings.private.securityGroupIds.private[0]': should be string

这意味着我们修复了之前的错误,但现在我们遇到了同样的问题,但降低了一级,因为 fn::split: 是一个 YAML 对象/字典。您还可以添加另一个列表项,例如:

        private:
          - blah # add this line
          - fn::split:
            delimiter: ','

..并且您将看到错误消息更改为引用索引 [1]。要证明 fn::split 值并不特殊,请将其更改为其他值,例如:

        private:
          - blah
          - asdf:  # change this line
            delimiter: ','

...你会看到错误信息没有改变。这是因为 Serverless 不关心 CloudFormation 函数,它只是检查 YAML 模式匹配。