3 min.

How to use environment variables in AWS SAM

Comprehensive Guide to Handling Environment Variables in AWS SAM for Deployment and Local Development

If you are accustomed to using environment variables (envs) similarly to other frameworks, you may be surprised to find that AWS SAM does not support the .env convention. Unfortunately, it employs a more complex and less intuitive system for handling envs.

 

The handling of envs varies depending on whether you use SAM deploy or sam local start-api / sam local invoke.  For both deployment and local executions, envs must be declared as parameters in the SAM template in the same manner. The difference lies in the methods available for passing envs from the local environment to the templates.

 

Consider the template below, which declares one parameter - EnvironmentType, used by both API Gateway and Lambda. Note the difference in passing parameters to these services. 

 

For API Gateway, we pass a parameter for the service configuration, whereas for Lambda, we pass a parameter that the code within Lambda executes.

 

 

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-layers-with-sam
Parameters:
  EnvironmentType:
    Type: String
Resources:
  API:
    Type: AWS::Serverless::HttpApi
    Properties:
      Name: API
      StageName: !Ref EnvironmentType
      DefaultRouteSettings:
        DetailedMetricsEnabled: true
        ThrottlingBurstLimit: 5
        ThrottlingRateLimit: 5
      CorsConfiguration:
        AllowMethods:
          - GET
          - OPTIONS
          - POST
          - PUT
          - HEAD
          - PATCH
          - DELETE
        AllowHeaders:
          - "*"
        AllowOrigins:
          - "*"
      FailOnWarnings: true
  CustomSlugifyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: lambdas/custom-slugify
      Handler: app.customSlugifyFunction
      Environment:
        Variables:
          EnvironmentType: !Ref EnvironmentType
      Events:
        ApiEvent:
          Type: HttpApi
          Properties:
            Path: /custom-slugify/{slug}
            Method: get
            ApiId:
              Ref: MainAPI
            Auth:
              Authorizer: NONE
      Runtime: nodejs18.x
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        Sourcemap: true
        EntryPoints:
          - app.ts

 

 

In Lambda, the parameter will be accessible as process.env.EnvironmentType. For example:

 

 

export const customSlugifyFunction = async (): Promise<{ body: string; statusCode: number }> => {
    try {
        return {
            statusCode: 200,
            body: JSON.stringify({
                message: customSlugify(process.env.EnvironmentType as string),
            }),
        };
    } catch (err) {
        console.log(err);
        return {
            statusCode: 500,
            body: JSON.stringify({
                message: 'some error happened',
            }),
        };
    }
};

 

 

For Lambda, it must always be declared in the Environment, Variables section. Alternatively, you can declare envs for all lambdas globally in the template as shown below.

 

 

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-layers-with-sam
Globals:
  Function:
    Environment:
      Variables:
        EnvironmentType: !Ref EnvironmentType
Parameters:
  EnvironmentType:
    Type: String

 

 

Now, let's explore how to pass values for these parameters declared in the SAM template.

 

 

SAM Deploy

 

When deploying the SAM template, the samconfig.toml file is typically used for passing environmental variables, specifically through the parameter_overrides key as follows:

 

 

\\ samconfig.toml

parameter_overrides = "EnvironmentType=\"develop\""

 

 

Each pair is separated by space. This file is not necessary; you can also use sam deploy --guided and then pass variables through an interactive console, with the option to save what you inserted to the samconfig file for future sessions.

 

Alternatively, you can name the config file differently and pass it with sam deploy --config-file customNameConfig.toml.

 

The well-known classic environmental won't work, but they can be passed directly through the CLI:

 

 

sam deploy --parameter-overrides EnvironmentType=develop

 

 

Finally, envs can be passed using a custom script in the following way:

 

 

#!/bin/bash
EnvironmentValue=$1

sam deploy --parameter-overrides EnvironmentType=$EnvironmentValue

 

 

Then, if the script is named awsDeployer.sh:

 

 

./awsDeployer.sh develop

 

 

SAM Local Invocation

 

SAM local can also use samconfig.toml that is used for SAM deploy. But additionaly you can pass environmental variables though specific JSON file using -env-vars argument with the following structure:

 

 

{
  "Parameters": {
    "EnvironmentType": "development",
  }
}

 

 

Parameters or reference to functionName inside the file is required in order for the variable to work. Then, your variable and values follow; the example declares EnvironmentType
 

 

{
  "CustomSlugifyFunction": {
    "EnvironmentType": "development"
  }
}

// OR
{
"CustomSlugifyFunction": {
    "EnvironmentType": "development"
  }
}

 

 

In the SAM template, it's not necessary to declare variables in the 'Parameters' section before the 'Resources' section, unlike what you do for samconfig.toml.

 

After defining variables in the proper JSON format, you can immediately reference these variables in your Lambda function using the process.env pattern.

 

The name for envs in JSON format is arbitrary, but I propose env.json for simplicity. In order to make SAM detect the file, it needs to be passed as a parameter in the following way:

 

 

sam local start-api --env-vars env.json

 

 

Or, if invoking a single Lambda:

 

 

sam local invoke CustomSlugifyFunction --env-vars env.json

 

 

Use Cases

 

Generally, utilize samconfig.toml for declaring any parameters, including your environment variables. This strategy, in combination with the --config-env argument that is applicable for both deployment and local testing, facilitates easy switching between different configuration sets. These sets are defined as follows:

 

 

version = 0.1

[default.global.parameters]
parameter_overrides = "EnvironmentType=\"production\""

[default.deploy.parameters]
parameter_overrides = "EnvironmentType=\"production\""

[default.local_start_api.parameters]
parameter_overrides = "EnvironmentType=\"development\""

[default.local_start_lambda.parameters]
parameter_overrides = "EnvironmentType=\"development\""

 

 

With such a declared samconfig.toml file, for local testing, using sam local start-api will always set EnvironmentType to ‘development’, but when deploying, it will take the 'production' value.

 

 

The sole use case for --env-vars is when you wish to locally override any parameters declared in the samconfig.toml file, as it always takes precedence. Additionally, targeting a specific functionName in the JSON config allows for passing different values to multiple functions that require the same environmental variables simultaneously.

 

 

{
  "CustomSlugifyFunction": {
    "SomeEnvironmentName": "value1"
  },
  "SlugifyFunction": {
    "SomeEnvironmentName": "value2"
  }
}

 

 

Lastly, if you wish to use specific envs only for a specific environment, you could prepare a samconfig.toml file like this:

 

 

version = 0.1

[default.local_start_api.parameters]
parameter_overrides = "EnvironmentType=\"development\""

[testing.local_start_api.parameters]
parameter_overrides = "EnvironmentType=\"not development\""

 

 

With the following command:

 

 

sam local start-api --config-env testing

 

 

EnvironmentType will take the 'not development' value."

 

 

For the record, if you encounter a similar error:

 

Error: Error reading configuration: Unexpected character: 'b' at line 22 col 15

 

 

This error almost certainly means that there is a mistake in your samconfig.toml file, likely due to a missing set of quotes around a variable value. For example:

 

You might have:

 

region = us-east-1

 

 

Instead of the correct format:

 

region = "us-east-1"