Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A simple approach to multi-environment configurations for AWS Lambda functions

AWS recently released Versioning and Aliases for Lambda Functions. I'm going to outline how I've taken advantage of this to provide environmentally-aware Lambda function configurations in Python.

AWS Lambda doesn't currently support environment variables, so 12-factor-style configuration isn't an option. I'll be using seprate config files for each environment.

Pregame

We're making two assumptions for this article:

  • I've already created an AWS Lambda function with the following aliases:
    • Dev
    • Test
    • Staging
    • Production
  • I've put together a deployment routine that will create and deploy a Python Deployment Package

The configuration files

My (simplified) project folder structure looks like this:

config/
|-- Dev-config.py
|-- Test-config.py
|-- Staging-config.py
|-- Production-config.py
lambda_utils.py
myfunction.py
  • My Lambda function handler is in myfunction.py
  • The config directory contains specific *-config.py files for each Lambda function alias/environment
  • The lambda_utils.py module contains the code used to inject environment-specific values into the Lambda function handler

As an example, here's what my Dev-config.py file looks like:

class MyName:
    first_name = "Bandit"
    last_name = "Brandit"

The configuration code

I'm using a Python Function Decorator to inject a config argument into my Lambda function handler.

Let's start with a basic handler in myfunction.py:

def myfunction_handler(event, context):
    message = 'Hello {} {}!'.format(event['first_name'], 
                                    event['last_name'])  
    return { 
        'message' : message
    }

I'm going to add the @import_config decorator that injects the environment-specific configuration:

from lambda_utils import *

@import_config
def myfunction_handler(event, context, config):
    message = 'Hello {} {}!'.format(config.MyName.first_name, 
                                    config.MyName.last_name)  
    return { 
        'message' : message
    }

Here's the @import_config decorator code in lambda_utils.py:

def get_env(context):
    # get the Alias name from the Lambda Function ARN
    split_arn = context.invoked_function_arn.split(':')
    env = split_arn[len(split_arn) - 1]
    return env

def import_config(f):
    def wrapper(*args, **kwargs):
        context = args[1]
        env = get_env(context)
        config = __import__(env + '-config')
        args += (config,)
        return f(*args, **kwargs)
    return wrapper

Conclusion

That's it! Put your *-config.py and lambda_utils.py files into your deployment package and you're good to go.

In the real world, I've used this pattern to integrate with environment-specific DynamoDB tables. It is also quite handy when defining configurations for local development (i.e. each developer has their own config). More on local Lambda function development to come...

@bobby259

This comment has been minimized.

Copy link

commented Mar 22, 2017

Hey Patrick,
Can u tell me how did u put together a deployment routine for lambda functions and from where would u push the updates?
Thank u!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.