AWS Lambda configuration scheme
Building servlerless applications with AWS lambda and not sure how to setup your configuration per environment? Here’s an approach that keeps things neat and tidy, yet allows for secure configuration values as well.
For the example we’ll be using the servlerless framework, but you could use a similar approach with SAM.
Configuration
With Lambda each environment is a stage. We’ll setup each stage (aka environment) in our serverless config like this:
service: my-service custom: default: prefix: my-service-prefix db_user: ssm://${self:custom.default.prefix}-${self:provider.stage}-db-user db_pwd: ssm://${self:custom.default.prefix}-${self:provider.stage}-db-pwd dev: db_host: host.hash.us-west-2.rds.amazonaws.com db_user: ${self:custom.default.db_user} db_password: ${self:custom.default.db_pwd}n_dev_1 db_database: my-service-db provider: name: aws runtime: nodejs8.10 stage: ${opt:stage, 'dev'} region: us-west-2 environment: db_host: ${self:custom.${self:provider.stage}.db_host} db_user: ${self:custom.${self:provider.stage}.db_user} db_password: ${self:custom.${self:provider.stage}.db_password} db_database: ${self:custom.${self:provider.stage}.db_database}
In the provider section we’ll set the stage by defaulting to ‘dev’ or taking the stage from the command line. This stage is referenced elsewhere to control stage specific settings. In the custom section we’ve define defaults and settings for the dev stage.
Server Side
Take note of how some values are plain text (db_database) and other values have the prefix ssm://. The ssm values have names that start with the service prefix and include the stage in the path. This prevents ssm name collisions between services and stages within a single service.
To accomplish this you’ll need some javascript code in your lambda:
var AWS = require('aws-sdk'); var ssm = new AWS.SSM({region: 'us-west-2'}); module.exports = { getEnvironment: async function (paramName) { let value = process.env[paramName]; if (value.substr(0, 6) === 'ssm://') { var params = { Name: value.substr(6, value.length), WithDecryption: true }; let request = await ssm.getParameter(params).promise(); value = request.Parameter.Value; } return value; } };
To reference an environment variable use this code (assuming the code above is imported as env):
let user = await env.getEnvironment('db_user');
Conclusion
This approach allows for configuration values to be set as environment variables or ssm values interchangeably. It also provides for default values that can be easily changed per environment.