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"