Serverless Environment Variables

Environment variables are now fully supported in AWS Lambda and the Serverless Framework. We’re going to see how to use them in our projects.

First up, make sure you are running the latest serverless CLI package. At the time of this writing, the lastest is v1.4.0.

$ serverless -v
1.4.0

Let’s create a folder for our project.

$ mkdir serverless-environment-variables
$ cd serverless-environment-variables

At the core of the Serverless Framework is the serverless.yml file which outlines your entire service. In our case, we will have a small handler for demo purposes:

# serverless.yml
service: serverless-environment-variables
provider:
name: aws
runtime: nodejs4.3
functions:
hello:
handler: handler.hello

Here we see the name of our service, which provider and runtime we want to use. Currently, only AWS is supported but I believe Google, Azure, and IBM support is in the works.

We only have one function for this demo and we call it hello. The handler property simply indicates where the code for that function is. In this case it is in a file called handler.js which exports a function called hello. Let’s write handler.js now.

// handler.js
'use strict';
module.exports.hello = (event, context, callback) => {
const response = {
foo: 'bar',
};
  callback(null, response);
};

Here, all we are doing is returning a response with the property foo and value bar.

This should be all you need to run your service.

$ serverless invoke local -f hello

should yield

{
"foo": "bar"
}

Good. Now we have a working Serverless service. Let’s add those environment variables in there. Open the serverless.yml file and add the bolded line.

# serverless.yml
service: serverless-environment-variables
provider:
name: aws
runtime: nodejs4.3
environment:
MY_VAR: abc
functions:
hello:
handler: handler.hello

In your handler.js, add the new environment variables to the response.

// handler.js
'use strict';
module.exports.hello = (event, context, callback) => {
const response = {
foo: 'bar',
MY_VAR: process.env.MY_VAR,
};
  callback(null, response);
};

Running the function again yields:

$ serverless invoke local -f hello
{
"foo": "bar",
"MY_VAR": "abc"
}

Nice!

Adding environment variables in the serverless.yml file is okay for non-sensitive information. Remember, you are commiting the serverless.yml file in your repo so no sensitive information should be present there. Next, we’ll explore another way for us to add sensitive information such as db connections for example.

Let’s create a new file called serverless.env.yml. This file will hold our sensitive variables.

# serverless.env.yml
dev:
SECRET_VAR: 'secret in dev'

Here we are creating a top level property dev and a variable within it. Now we need a way to reference this file in our main serverless.yml file.

# serverless.yml
service: serverless-environment-variables
provider:
name: aws
runtime: nodejs4.3
environment:
MY_VAR: abc
SECRET_VAR: ${file(./serverless.env.yml):dev.SECRET_VAR}
functions:
hello:
handler: handler.hello

What happens here is that our secret variable will be a reference to the serverless.env.yml file which will not be commited to the repo. As a result, it may contain sensitive data like the connection string to our database.

Don’t forget to add the new variable to your handler.js file.

// handler.js
'use strict';
module.exports.hello = (event, context, callback) => {
const response = {
foo: 'bar',
MY_VAR: process.env.MY_VAR,
SECRET_VAR: process.env.SECRET_VAR,
};
  callback(null, response);
};

Running the function again should produce:

$ sls invoke local -f hello
{
"foo": "bar",
"MY_VAR": "abc",
"SECRET_VAR": "secret in dev"
}

And there’s the secret variable available inside your function. Obviously you should not return this in your response in a real scenario.

Lastly, you can have environment variables for each deployment stage such as dev and prd. Let’s add a prd property to the serverless.env.yml file.

# serverless.env.yml
dev:
SECRET_VAR: 'secret in dev'
prd:
SECRET_VAR: 'secret in prd'

Now we need to tell Serverless to dynamically pick the correct variable based on the stage we set when we run or deploy the function. We need to make one small change in the serverless.yml file to accomplish this.

# serverless.yml
service: serverless-environment-variables
provider:
name: aws
runtime: nodejs4.3
environment:
MY_VAR: abc
SECRET_VAR: ${file(./serverless.env.yml):${opt:stage}.SECRET_VAR}
functions:
hello:
handler: handler.hello

We replaced dev with ${opt:stage} which will get interpreted and replaced with the stage name we choose. Now when we run with the -s flag, we can specify the stage and get the correct variable for that stage.

$ sls invoke local -f hello -s dev
{
"foo": "bar",
"MY_VAR": "abc",
"SECRET_VAR": "secret in dev"
}
$ sls invoke local -f hello -s prd
{
"foo": "bar",
"MY_VAR": "abc",
"SECRET_VAR": "secret in prd"
}

I hope this was helpful.

You can find all the code mentioned here https://github.com/purplecones/serverless-environment-variables for reference.