aws-sam-cli: SAM deploy doesn't set environment variables
Description
When using AWS SAM for local development, I can introduce environment variables by setting them in the template with no value, and then defining them in my environment. However, when I go to deploy, the environment variables do not appear to be inserted by sam package
or sam deploy
, and I get the following error on deploy:
Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: [/Resources/GeneratePlan/Type/Environment/Variables/SECRET_ACCESS_KEY] 'null' values are not allowed in templates
Where SECRET_ACCESS_KEY
is one of my environment variables. I cannot find any documentation detailing how to deploy projects with environment variables, either by having them defined in my environment or providing them in an alternate config.
I don’t want to add the environment variables to my template.yml
directly because this is stored in Git, and I don’t want to edit them manually into the packaged.yml
file each time between the package
and deploy
steps as that’s cumbersome.
I haven’t seen any steps in the documentation or similar issues, so I presume this is either an edge case bug or I am just missing something simple (in which case I might file this as a documentation bug) 😄
Steps to reproduce
The following config is a minimal excerpt from mine:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Environment:
Variables:
SECRET_ACCESS_KEY:
Resources:
AnswerChallenge:
Type: "AWS::Serverless::Function"
Properties:
Runtime: nodejs8.10
Handler: build/functions/answerChallenge.default
CodeUri: ./
Policies: AmazonDynamoDBFullAccess
Events:
GetRequest:
Type: Api
Properties:
Path: /event
Method: get
Observed result
When I run sam start-api
with SECRET_ACCESS_KEY
defined, the lambda works as expected. When I attempt to deploy with sam package
and sam deploy
, I receive an error about undefined variables.
Additional environment details (Ex: Windows, Mac, Amazon Linux etc)
- OS: MacOS
sam --version
: 0.11
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 40
- Comments: 48 (7 by maintainers)
Yeah the
sam local start-api
also uses the mismatched format in--parameter-overrides
, which is frustrating! Having to use two different config formats between local dev and prod deploy is not great… I had tried the--env-vars
solution but had issues with the different cases expected between uppercase and snake case. This is what I had to do to get some resemblance of consistency:I store my config file as a JSON file, such as
config.json
:And define them as parameters in the
template.yaml
:Then for development I have the following in
package.json
:So I can run
npm run api
to get the api server going with the config file.Then for deployment our CI runs:
Surprising amount of work to get sane environment variables in SAM… I think this needs some developer experience TLC or maybe I’m missing something blatantly obvious 😅
Same issue here.
sam package
/sam deploy
should accept the--env-vars
flag.My current workaround is to use CF parameters, since
sam package
/sam deploy
are aliases forcloudformation package
/cloudformation deploy
.template.yml
I too expected SAM would have a consistent way to handle env vars between running locally and deploying. Like @jsonmaur I expected
--env-vars
to work for deployment too.Right now
sam local
,sam build
, andsam deploy
have three, mutually incompatible ways to specify environment variable values.I agree that documentation could be better - the most important is to add missing param
--parameter-overrides
toaws deploy
command.sam deploy
is an alias toaws cloudformation deploy
, I think - when you runaws cloudformation deploy help
you can find information about missing param.Working example
template.yaml
When you run:
the default value will be used - in our case:
SOME_VAR=default value
To deploy run commands:
The last line do “the magic” and allow you to override default value:
When the real function will be invoked:
SOME_VAR=other_value
.I think is important to remember that SAM templates are compatible with CloudFormation templates and all techniques/concepts could be used in SAM development.
@awood45 Thanks for jumping in! I will see if I can provide some context. Essentially, I’m looking for a unified method for configuring the application that works both in sam local and sam deploy.
For example, when I create a non-serverless Node.js application based on express, in the documentation I can provide information on what environment variables are expected to be present. Developers can define these in their environment for local dev, and in production the server can set those environment variables. It’s the same interface, it’s well defined intuitively, and it’s conventional.
I thought I had similar behaviour in SAM, as I found a Github issue mentioning how to pass through environment variables by setting them to an empty value in the template. That was great! It was frustrating to learn this does not work when deploying.
Some notes on my goals:
jq
to get them to parse from a file. This is particularly annoying as sam local and sam deploy expect them on the command line in different formats.FWIW I think there is both dev UX aspects and documentation aspects here. I have ideals about how to deploy, which would be great if we could improve the dev UX there, but realistically documentation is what originally made me create the issue here. When I went to figure out how to add environment variables in dev I couldn’t find any documentation on this at all. I did find an answer through Github issues. When I found that didn’t work in production, I again couldn’t find any documentation at all on how to actually deploy environment variables, and had to get help on this issue here from @jsonmaur to get deploy working. It may seem intuitive as it uses another AWS command under the hood, but do consider that users of SAM for the first time might not be familiar with how to deploy lambda’s under cloudformation. This has been my experience over many months so it’s possible that docs have improved since then but I definitely found it unclear how to actually deploy an app with env variables.
Does that clarify a bit? Happy to help elaborate some more if needed.
Wow does SAM make setting environment variables complicated!
@jsonmaur’s workaround seems the best, using the (undocumented in sam docs)
--parameter-overrides
argument tosam deploy
using theName1=Value1 Name2=Value2
syntax.sam build
documents a--parameter-overrides
argument, but it uses the the JSON shorthand syntax:ParameterKey=KeyPairName, ParameterValue=MyKey ParameterKey=InstanceType, ParameterValue=t1.micro
.So
sam
andcloudformation
have multiple same-named arguments with entirely incompatible value syntax? Not helpful!I tried using the
sam build --parameter-overrides
option but the parameters don’t see to go into the build, and the values you specify for build are lost when you deploy. So not even sure what that is for?One item has been resolved 🥳
sam deploy --parameter-overrides
), this is now documented ✅The core consistency problem with
sam
remains:To add more context to the “We have 3 different ways to do environment variables”.
The
--env-vars
options pre-dates use resolving Parameters. During that time, we needed a way to allow customers to change their Env Vars defined in the template. This still has applications today (overriding dynamic references that are inlined in the Env Var definition for example) but most of the time--parameter-overrides
will be what you will want. Especially, if you are following the pattern of using Parameters to allow changes to the template at deploy (or in this case local invoke) time.Now to the "build and local have different way than deploy for
parameter-overrides
.Sadly, we didn’t have a better way. Currently deploy still shells out to the AWS CLI (which we will be addressing separately). When we introduced the
--parameter-overrides
into the local suite (and later build), we attempted to match what AWS CLI was doing to keep things consistent. AWS CLI, however, does not use click (they use argparse if I recall correctly). Because the the different ways we have the cli command defined, we couldn’t match AWS CLI’s way and were forced to deviate. When we revisit deploy, this will most likely change to follow the pattern we have in local/build.Hopefully that sheds some light on why, it is what it is right now. We know the inconsistency is a UX problem and rough edge right now.
So why we can’t set
Parameter overrides
directly insamconfig.toml
file as well?--parameter-overrides
is inconvience, because I need set 20+ parameter key pair.and these key/values are plain text, no sensitive password, So I prefer to put in
samconfig.toml
directly, such asand I can easily add other environments as staging, prod, etc.
Update 1
I made it. We can set
parameter_overrides
directly insamconfig.toml
When
sam deploy --config-env dev
, I can see the part ofParameter overrides
is not empty any more.Of course, you can add
--parameter_overrides
in command line as well for some of them.reference: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Update 2
I found an easy way to set
parameter_overrides
insamconfig.toml
(Notice the escape)Both ways are working
Update 3
I made a repo to show case on how to manage the parameter with seperate files for different environments.
Take a look, if you are interested.
https://github.com/ozbillwang/sam-hello-world
Right. I think the main reason against using the samconfig.toml is the secrets issue since you will most likely want to commit it to git. If you add to .gitignore than no problems, works great. Otherwise you could have secrets committed to your repo.
Of course creating scripts with the parameter overrides in your package.json will also add secrets to your git repo as well.
Dotenv and cross-var
I prefer using a .env file then using doting and cross-var to create scripts in my package json.
yarn dotenv -e .env.prod -- cross-var sam deploy --stack-name NAME-OF-STACK --parameter-overrides NodeEnv=prod parameter1=%PARAMETER1% parameter2=%PARAMETER2% parameter3=%PARAMETER3%
Then you can have multiple .env files for each environment (.env.dev, .env.prod, .env.uat) and create a package json script for each (I use start for dev and
sam local start
in the script instead ofsam deploy
, deploy-prod, deploy-uat).That way I am using an industry standard .env file that can hold secrets and even though the package.json scripts can get quite large, I’m not editing them often so I can just use the yarn scriptname and I’m good to go.
Then it’s always good practice to add .env.example files as well that will be committed to git so anyone cloning the repo will at least know what environment variables are required.
Hope that gives another possible way of organizing.
Hi @bvisan,
The
local-invoke
command works fine on my local. Did you list your environment variables name in the template? Here is mytemplate.yaml
For local testing, you can override env variables in a few ways, including the
--env-vars
parameter. However, in thedeploy
step, you’re just passing along the packaged template. For that, you must define all environment variables, either directly or as a parameter.If this doesn’t cover your needs, it could help to explain more about what you’re trying to do.
Another year passed,
sam deploy
still can’t read env vars in a file.But here’s a workaround. Assuming your project’s deployment parameters (e.g. environment variables) are maintained in a
.env
file like this:You can create an alias like this:
Not a perfect solution (e.g. easily breaks with an extra space), but better than nothing.
Better yet, if you are working on a nodejs project like me, you can totally use
npm
to take over development lifecycle.In
package.json
:Reading through the comments, I believe this is solve.
To summarize: SAM CLI will not set environment variables in the template. Instead for
sam deploy
you use--parameter-overrides
, which tells CloudFormation what the values are. For local, you use--env-vars
to set the environment variables to a testing value.If I have a miss understanding of the issue, please create a new issue (closed issues are hard to track).
Coming to this issue from Google, I’m still confused what the intended way to handle env vars is. I tried something like this (based on a few blogs):
I then want to be able to configure a different config (i.e. a set of env vars) for:
sam build
andsam local invoke
I would imagine this is an MVP setup for any project.
We’re also using Python if that makes a difference.
Is there a simple way to do this? I thought the above would work, but when using build + invoke the environment variables are set to their names (e.g.
os.environ['API_KEY']='API_KEY'
).btw @mudassirkhan19, it didn’t work for me even with CamelCase.
Quick clarification here and likely we need to update some docs.
--parameter-overrides
does not set the values in the template on build or deploy. Onsam build
,parameter-overrides
is used as a way to tell SAM CLI to resolve the parameters in the template so the build does not fail (like passing is a parameter forCodeUri
orRuntime
, etc. Onsam deploy
,parameter-overrides
is passed to CloudFormation who does the full resolving.parameter-overrides
is not use to set the value back into the template.@m-sureshraj That did the trick 👍🏼
Has anyone found a good way to address this with quite a few different env variables (say 10-20 or so)?
The two things I want to avoid are:
The only ways I could think of would be adding them to the
template.yaml
file or adding the script with the--parameter-overrides
topackage.json
file. Both would mean I have to commit my env variables to a repo.Thanks @jfuss! I got it deployed. I found that I then had to add the environment variable using the Web UI. Here’s what worked.
in template.yml
contents of local invoke config.json
in the app.js, a value of process.env.KEY1 is available
Hello, @rebeccapeltz
For multiple parameter overrides, you could write it like this (without comma, just space):
We’ll definitely want to improve documentation on the deploy difference, though I would agree we should at minimum converge options on build/local to match.
One issue with deployments is that they are simply a CloudFormation deployment. What you’d be asking for in that case is for
--env-vars
to transform the template before deployment, without those changes being saved anywhere.Unfortunately the ship has sailed to an extent for naming this better without it being a breaking change, but it’s really a way to override your production env variables for local testing.
Is there any response to this? I’m seeing the same thing.
sam package
creates a YAML file containing my environment variables as expected, but after runningsam deploy
I can see that the Lambda configuration in the AWS console hasn’t been updated to include them.Trying to execute the Lambda then fails with the predictable “environment variable not defined” result.
Works fine for me when I run
sam local invoke --env-vars env.json
.--env-vars
seems to override the parameters.