Using fn::split in Serverless yaml configuration not working

Viewed 2248

I'm using serverless framework to deploy an API on AWS. I have the following in my serverless.yml file:

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

VPC_SG_ID contains the following string: sg-1111111111,sg-222222222,sg-3333333333

However, when deploying the application, I get the following error: An error occurred: MyLambdaFunction - Value of property SecurityGroupIds must be of type List of String.

If I hardcode the SGs list, it's working without any issue:

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

Why the fn::split function is not returning a list of strings?

Edit:

The following configuration results in the same error

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

If Security group is added as Input parameter to template

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

Security Groups can be split with !Split as

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

can be split with Fn:Split as

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

Parameter to sam deploy can be passed as

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

I'm trying to achieve the same thing but I'm pretty sure you can't use CloudFormation functions (Fn::Split) in serverless.yml.

Alternative solution 1

Passing YAML list/arrays through env vars doesn't seem possible for serverless because we can't write code to parse the strings that come from env vars. Maybe you could write a serverless plugin, but I haven't looked into that.

I assume you need to be able to pass lists of variable length and arbitrary values, so the following solution might not work great for you.

If you have known values, you can keep the lists in serverless.yml and select a list with an env var:

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

...then you select a list by:

LIST_SELECTOR=list1 sls print
LIST_SELECTOR=list2 sls print

If you need arbitrary values, and your lists don't get too long, then you could get really hacky and make each list item an 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}}

..and supply everything as env vars:

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

Alternative solution 2

You could also leverage the Serverless feature to reference properties in other files. You can create a small YAML file (that's ignored from version control):

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

...and then reference that file in your serverless.yml:

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

Explaining the error message

I think your error message is referring to the fact that you're passing a YAML object/dictionary into private when it wants a YAML list/array. The key thing is this is all to do with YAML syntax. The Fn::Split function is something specific to AWS CloudFormation and I guess the "execution" of this function happens within AWS infrastructure.

We can test this theory by changing your code to supply a list:

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

...then you'll see an error like

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

This means we fixed the previous error but now we have the same problem but one level down because fn::split: is a YAML object/dictionary. You can also add another list item, e.g:

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

..and you'll see the error message change to reference index [1]. To prove the fn::split value isn't special, change it to something else, e.g:

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

...and you'll see the error message doesn't change. This is because Serverless doesn't care about the CloudFormation functions, it's just checking the YAML schema matches.

I had an exact same problem, where I was doing split as below, but it was not working

   Fn::Split:
        delimiter: ','
        value: ${cf:mystack.ClusterSubnets}

It was giving split errors like

Split object requires two parameters, (1) a string delimiter and (2) a string to be split

I changed it to the following and it worked

    Fn::Split: 
        - ','
        - ${cf:mystack.ClusterSubnets}
Related