62

I am following the readme here: https://github.com/awslabs/aws-sam-local

I have a lambda written in python 3.6 and its similar to the helloworld example here : https://github.com/awslabs/aws-sam-local/tree/develop/samples/hello-world/python

template.yml looks as such:

AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: MyFunction1 API
Resources:
  MyFunction1:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: MyFunction1
      Handler: lambda_module.lambda_handler
      Runtime: python3.6
      CodeUri: lambda.zip
      MemorySize: 128
      Timeout: 10
      Policies:
        -AWSLamdbaBasicExecutionRole
      Events:
        BingLambdaEndpoint:
          Type: Api
          Properties:
            Path: MyFunction1/search
            Method: get

I have environment variables within the lambda but not able to wire them up on start up. Documentation says I can create a environments.json file and append the following on the invoke command : Use --env-vars argument of invoke

My environment file looks like the example and I get an error: Unable to find environment variable: api_key

environment.json looks as such:

{
  "MyFunction1": {
    "api_key": "123456789",
    "BUCKET_NAME": "testBucket"
  }
}

command I run is as such:

sam local invoke MyFunction1 --env-vars environment_variables.json -e event.json

Can anyone provide additional insight?

Mike Patrick
  • 10,699
  • 1
  • 32
  • 54
gotjava2012
  • 661
  • 1
  • 6
  • 7

5 Answers5

86

Any environment variables you want to use with SAM Local in this manner need to exist in your SAM template. From this GitHub issue:

... SAM Local only parses environment variables that are defined in the SAM template.

In the template, you can provide no value, an empty string, or choose a sensible default.

A portion of a template including environment variables:

Resources:
  MyFunction1:
    Type: 'AWS::Serverless::Function'
    Properties:
      .....
      Environment:
        Variables:
          api_key:
          BUCKET_NAME:

You can think of the environment variables file as a mechanism to override environment variables that the template "knows" about. It does not work as a mechanism to inject arbitrary environment variables into the local runtime.

Daryn
  • 4,791
  • 4
  • 39
  • 52
Mike Patrick
  • 10,699
  • 1
  • 32
  • 54
  • 1
    I understand that, but when running the lambda it seems to have no knowledge of the environment variables. However, I can configure them in the template file and it works. I would like to use a separate config file to house my variables as outlined in the Sam local docs. – gotjava2012 Feb 02 '18 at 23:39
  • 2
    I found this from google when looking for a reference to using environment variables. According to the [docs about sam local environment variables](https://github.com/awslabs/aws-sam-cli/blob/develop/docs/advanced_usage.md#lambda-environment-variables): The `Environment` key defines the variables and the default values. One can use the `--env-vars` cli argument to define a json file to override the values. I will update the answer with this info. – Frank Robert Anderson Feb 18 '19 at 23:19
  • 1
    You can also apply this at a Global level: `Globals: Function: Environment: Variables: api_key:` – James Burke Sep 22 '19 at 16:21
45

template file

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
  
Globals:

  Function:
    Timeout: 3

Parameters:

  SomeVar:
    Type: String
    Description: My SomeVar
    Default: default value

Resources:

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs12.x
      Environment:
        Variables:
          SOME_VAR: !Ref SomeVar
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

Outputs:

  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"

  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn

  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

from https://github.com/awslabs/aws-sam-cli/issues/1163#issuecomment-557874976

then in code

console.log(process.env.SOME_VAR);

when you run sam local start-api and it will print default value

when you run sam local start-api --parameter-overrides SomeVar=other_value and it will print other_value

then if you create file env.json with this content

{ "PreviewsFunction": { "SOME_VAR": "123" } }

when you run sam local start-api --env-vars env.json and it will print 123

p.s. it will work with start-api/start-lambda/invoke all in the same way, but it looks like sam deploy only works with --parameter-overrides SomeVar=other_value and no --env-vars

Daryn
  • 4,791
  • 4
  • 39
  • 52
JLarky
  • 9,833
  • 5
  • 36
  • 37
12

Ensure that the variables are declared in template.yml. A config file overwrites the variables but does not create variables when they don't exist in the original template.

toonsend
  • 1,296
  • 13
  • 16
  • Thank you! I kept trying to pull in env variables with sam build --use container --container-env-var-file env.json, but I didn't have those variables pre-declared in template.yaml file so it kept failing – James Lin Feb 01 '23 at 23:09
4

I had the same issue. When I ran

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

The values in env.json where not being read. What fixed the issue for me was to use the following format in env.json

"Parameters": {
    "PARAM_NAME": "VALUE"
}

The other format did not work for me:

{ "function": { "PARAM_NAME": "VALUE" } }
hkutluay
  • 6,794
  • 2
  • 33
  • 53
Abul Fayes
  • 117
  • 5
1

Make sure that your template's parameters have NoEcho equals true, like in this example:

Parameters:
  # Build variables
  Stage:
    Type: String
  # Environment variables
  MssqlServer:
    Type: String
    NoEcho: true
  MssqlDatabase:
    Type: String
    NoEcho: true
  MssqlUser:
    Type: String
    NoEcho: true
  MssqlPassword:
    Type: String
    NoEcho: true

Then you can simply pass the environment variables with a command like this:

sam deploy --parameter-overrides "MssqlServer='$MSSQL_SERVER' MssqlDatabase='$MSSQL_DATABASE' MssqlUser='$MSSQL_USER' MssqlPassword='$MSSQL_PASSWORD' Stage=dev" --config-env dev
Leopoldo Varela
  • 257
  • 3
  • 9
  • I'm not 100% sure it applies here, but AWS recommends against putting secrets into "dynamically referencable values". See here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html "To determine which resource properties comprise a resource type's primary identifier, refer to the resource reference documentation for that resource. In the Return values section, the Ref function return value represents the resource properties that comprise the resource type's primary identifier." Unless that's what NoEcho is for? – Steven Staley Apr 16 '23 at 19:33
  • More info here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html "Rather than embedding sensitive information directly in your CloudFormation templates, we recommend you use dynamic parameters in the stack template to reference sensitive information that is stored and managed outside of CloudFormation, such as in the AWS Systems Manager Parameter Store or AWS Secrets Manager. For more information, see the Do not embed credentials in your templates best practice." – Steven Staley Apr 16 '23 at 19:36