In AWS Lambda, you can have multiple environment configurations such as dev, test, staging, production etc using AWS Lambda Aliases. Even if you don’t have aliases, you have 2 default configurations; 1) test when you are running the Lambda function locally using say VS Code and 2) prod when the function is run on AWS Lambda service.

aws lambda function examples python aws lambda python tutorial lambda python 3 aws lambda environment variables aws lambda environment variables python python aws lambda environment variables

But how to figure the current environment in AWS Lambda?

The answer is, from the context argument in the lambda handler. Whenever the Lambda function is invoked, the context argument has some attributes that let us know in which alias the Lambda function is currently running in.

This post shows how to have multiple environment configuration files and load those values so they can be used in the the codebase anywhere until the invocation of the Lambda function remains active.

Separate configuration files

Let’s say I have this directory structure:

- test-config.py
- prod-config.py
- lambdaHandler.py
- configLoader.py
- globalConstants.py
- src
  - ...
*-config.py

test-config.py & prod-config.py are my configurations for test and production environments respectively. They contain dictionaries like this:

class myConfig:
    mysql = { 'host': 'localhost', 'user': 'admin', 'password': 'p@$$word!', 'port': 3306, 'database': 'cars' }

Why I am using Python files for configuration instead of json or YAML is just my preference currently and I have my reasons which I will discuss in a separate blog post.

globalConstants.py

globalConstants.py contains global variables that can be accessed anywhere in the code:

mysql = ''

def __init__():
    global mysql
lambdaHandler.py

lambdaHandler.py is the Lambda handler.

lambdaHandler.py
from configLoader import lambdaConfig

@lambdaConfig
def lambda_handler(event, context):
    ...

Notice the @lambdaConfig, this is a Python decorator inside configLoader.py which I describe in the next section.

configLoader.py: Load configuration

configLoader.py contains my Python decorator function. I place this decorator on my lambda handler so that my current configuration file gets loaded before any statements inside the lambda handler get executed. A brilliant explanation of Python decorators can be found here.

So we build a Python decorator function:

configLoader.py
import globalConstants

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

def lambdaConfig(func):
    def wrapper(*args, **kwargs):
            context = args[1]
            environment = getEnvironment(context)
            config = __import__(environment + '-config')
            globalConstants.mysql = config.myConfig.mysql
            return func(*args, **kwargs)
    return wrapper

What I am doing here is that I am parsing the incoming context argument and within it I can see what alias of the Lambda function is currently being invoked.

After I figure the current environment, I load the corresponding config file; test or prod. Then I assign values to a global variable inside of globalConstants.py. I then return the current function upon which the decorator is placed(lambda_handler) so that it’s invocation can be resumed now.

Accessing values anywhere in code

This way, I am now able to use the current environment values from globalConstants.py from anywhere inside my code no matter which configuration got loaded:

import globalConstants

host = globalConstants.mysql["host"]
user = globalConstants.mysql["user"]
password = globalConstants.mysql["password"]

Alternatively, inject the config into Lambda function

As can be seen here, you can load the configuration and inject it into the Lambda function as well. All credit goes to that reference as this is the original idea behind my approach, I just didn’t want to inject the config in the Lambda function.

So just do this in your Python decorator:

def lambdaConfig(func):
    def wrapper(*args, **kwargs):
            context = args[1]
            environment = getEnvironment(context)
            config = __import__(environment + '-config')
            args += (config,)
            return func(*args, **kwargs)
    return wrapper

Instead of assigning to globalConstants, just add it to the arguments.

In the Lambda handler:

def lambda_handler(event, context, config):

Summary

This post describes how you can use environment variables using Python in AWS Lambda functions by having multiple environment configuration files and loading the corresponding configuration when that AWS alias is invoked. AWS Lambda environment variables are loaded by Python decorators but other languages can also avail it, e.g., C# attributes etc. It also shows 2 possible ways to get the current configuration values, by either using global variables in Python or by injecting the config into the Lambda function.