Skip to content

Homepage

AWS Lambda Environment Variables Parser Cookbook

alt_text

AWS-Lambda-Env-Modeler is a Python library designed to simplify the process of managing and validating environment variables in your AWS Lambda functions.

It leverages the power of Pydantic models to define the expected structure and types of the environment variables.

This library is especially handy for serverless applications where managing configuration via environment variables is a common practice.

The Problem

Environment variables are often viewed as an essential utility. They serve as static AWS Lambda function configuration.

Their values are set during the Lambda deployment, and the only way to change them is to redeploy the Lambda function with updated values.

However, many engineers use them unsafely despite being such an integral and fundamental part of any AWS Lambda function deployment.

This usage may cause nasty bugs or even crashes in production.

This library allows you to correctly parse, validate, and use your environment variables in your Python AWS Lambda code.

Read more about it here

Features

  • Validates the environment variables against a Pydantic model: define both semantic and syntactic validation.
  • Serializes the string environment variables into complex classes and types.
  • Provides means to access the environment variables safely with a global getter function in every part of the function.
  • Provides a decorator to initialize the environment variables before executing a function.
  • Caches the parsed model for performance improvement for multiple 'get' calls.

Installation

You can install it using pip:

1
pip install aws-lambda-env-modeler

Usage

Schema Definition

First, define a Pydantic model for your environment variables:

schema.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from typing import Literal

from pydantic import BaseModel, Field, HttpUrl

from aws_lambda_env_modeler.types import Annotated


class MyEnvVariables(BaseModel):
    REST_API: HttpUrl
    ROLE_ARN: Annotated[str, Field(min_length=20, max_length=2048)]
    POWERTOOLS_SERVICE_NAME: Annotated[str, Field(min_length=1)]
    LOG_LEVEL: Literal['DEBUG', 'INFO', 'ERROR', 'CRITICAL', 'WARNING', 'EXCEPTION']

Notice how you can use advanced types and value assertions and not just plain strings.

Decorator

Before executing a function, you must use the @init_environment_variables decorator to validate and initialize the environment variables automatically.

The decorator guarantees that the function will run with the correct variable configuration.

Then, you can fetch the environment variables using the global getter function, 'get_environment_variables,' and use them just like a data class. At this point, they are parsed and validated.

my_handler.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import json
from http import HTTPStatus
from typing import Any, Dict, Literal

from pydantic import BaseModel, Field, HttpUrl

from aws_lambda_env_modeler import get_environment_variables, init_environment_variables
from aws_lambda_env_modeler.types import Annotated


class MyHandlerEnvVars(BaseModel):
    REST_API: HttpUrl
    ROLE_ARN: Annotated[str, Field(min_length=20, max_length=2048)]
    POWERTOOLS_SERVICE_NAME: Annotated[str, Field(min_length=1)]
    LOG_LEVEL: Literal['DEBUG', 'INFO', 'ERROR', 'CRITICAL', 'WARNING', 'EXCEPTION']


@init_environment_variables(model=MyHandlerEnvVars)
def my_handler(event: Dict[str, Any], context) -> Dict[str, Any]:
    env_vars = get_environment_variables(model=MyHandlerEnvVars)  # noqa: F841
    # can access directly env_vars.REST_API, env_vars.ROLE_ARN as dataclass
    return {
        'statusCode': HTTPStatus.OK,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps({'message': 'success'}),
    }

Disabling Cache for Testing

By default, the modeler uses cache - the parsed model is cached for performance improvement for multiple 'get' calls.

In some cases, such as during testing, you may want to turn off the cache. You can do this by setting the LAMBDA_ENV_MODELER_DISABLE_CACHE environment variable to 'True.'

This is especially useful in tests where you want to run multiple tests concurrently, each with a different set of environment variables.

Here's an example of how you can use this in a pytest test:

pytest.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import json
from http import HTTPStatus
from typing import Any, Dict, Literal
from unittest.mock import patch

from pydantic import BaseModel

from aws_lambda_env_modeler import LAMBDA_ENV_MODELER_DISABLE_CACHE, get_environment_variables, init_environment_variables


class MyHandlerEnvVars(BaseModel):
    LOG_LEVEL: Literal['DEBUG', 'INFO', 'ERROR', 'CRITICAL', 'WARNING', 'EXCEPTION']


@init_environment_variables(model=MyHandlerEnvVars)
def my_handler(event: Dict[str, Any], context) -> Dict[str, Any]:
    env_vars = get_environment_variables(model=MyHandlerEnvVars)  # noqa: F841
    # can access directly env_vars.LOG_LEVEL as dataclass
    return {
        'statusCode': HTTPStatus.OK,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps({'message': 'success'}),
    }


@patch.dict('os.environ', {LAMBDA_ENV_MODELER_DISABLE_CACHE: 'true', 'LOG_LEVEL': 'DEBUG'})
def test_my_handler():
    response = my_handler({}, None)
    assert response['statusCode'] == HTTPStatus.OK
    assert response['headers'] == {'Content-Type': 'application/json'}
    assert json.loads(response['body']) == {'message': 'success'}

License

This library is licensed under the MIT License. See the LICENSE file.