Use environment variable as list in serverless.yml

I’m using serverless 1.15.3 to deploy a lambda function into a VPC and need to set different securityGroupIds and subnetIds for each deployment environment (dev, test, prod). For other configuration that depends on the deployment environment I’ve used environment variables that are set by the pipeline, however, the securityGroupIds and subnetIds are lists, and I haven’t been able to figure out how to specify values in the environment so they can be used within the serverless.yml configuration.

My preferred solution would define the vpc as follows in serverless.yml:

  custom:
    defaultSubnetIds: [subnet-12345678, subnet-9abcde01, subnet-23456789]
    defaultSecurityGroupIds: [sg-12345678]

  vpc:
    subnetIds: ${env:SUBNET_IDS, self:custom.defaultSubnetIds}
    securityGroupIds: ${env:SECURITY_GROUP_IDS, self:custom.defaultSecurityGroupIds}

Serverless is happy with this, as long as there’s SECURITY_GROUP_IDS and SUBNET_IDS are not defined in the environment.

However, I haven’t been able to find a way to set the value of either of the above environment variables so that serverless interprets them as a list of strings. Serverless gives the error Value of property SubnetIds must be of type List of String. when I try to set the environment variables following any of the following patterns:

SUBNET_IDS="[sg-1,sg2,sg3]"
SUBNET_IDS="['sg1','sg2','sg3']"
SUBNET_IDS="['sg1', 'sg2', 'sg3']"
SUBNET_IDS="[ sg1 sg2 sg3 ]"
SUBNET_IDS="[ sg1, sg2, sg3 ]"
SUBNET_IDS=(sg1 sg2 sg3)

Generally speaking, the shell requires the double quotes.

For the time being, I’m using a separate variable for each SUBNET_ID, but I find this dissatisfying because it is inflexible about the number of security groups that are defined.

Has anyone had any luck setting a serverless attribute list with an environment variable?

I’m not sure if you figured this out. It might be easier to have a different yaml file for each stage then include that based on the stage you’re deploying to.

vpc: ${file(config-${self:custom.stage}.yml):vpc}

Then inside the file you would have something like:

vpc:
  subnetIds:
    - subnet-12345678
    - subnet-9abcde01
    - subnet-23456789
  securityGroupIds:
    - sg-12345678
1 Like

Thanks buggy, that might be a suitable work around, though I’m still curious if it can be done from an environment variable.

Environment variables don’t support lists/arrays as a data type, so this is not really a Serverless limitation but a limitation of environment variables.

Since you know the content of the variable, why not just pass a string of comma separated ids, and split() them in your function?

Using split() within the function would be a very easy solution if the values were getting used in the lambda function, but in the scenario described above, the values are getting used in the serverless config to set up the VPC membership. What I really need is a way to split the string in the serverless.yml file.

Ah, my bad. The short answer is: No.

You could request it as a feature, but it would require a code change to the variables functionality.

Thanks for clarifying. At least now I know that my hacky solution is the best I can hope for with the version we’re using.

After I struggled for the same issue for 1 day, I found an accepted way of passing subnet ids as a list to VPC block.
In your case, variables are expected to be like this:

custom:
     defaultSubnetIds: 'subnet-12345678, subnet-9abcde01'
     defaultSecurityGroupIds: 'sg-12345678'

In vpc block, you can split the elements by a delimiter:

vpc:
   subnetIds:
     "Fn::Split":
        - ","
        - ${opt:SUBNET_IDS, self:custom.defaultSubnetIds}
   securityGroupIds:
     "Fn::Split":
        - ","
        - ${opt:SECURITY_GROUP_IDS, self:custom.defaultSecurityGroupIds}
3 Likes

Brilliant! Thanks for sharing.

Awesome :clap: :clap: :clap: :clap: :clap: :clap: :clap: