Question

How to access environment variable in lambda function using Amplify Gen2

I am unable to access env variable in lambda function. So far:

I've read official docs and my project looks like follow:

  1. I have function defined as follows:
export const dailyNotification = defineFunction({
  name: 'daily-notification',
  entry: './handler.ts',
  environment: {
    TESTZG: process.env.TESTZG ?? 'default',
  }
});
  1. In my handler I am able to access env through generated env symbol, as in docs.
import { env } from '$amplify/env/daily-notification';
(...)
console.log(`TESTZG, ${env.TESTZG}`);
  1. I have this environment variable added in Amplify console:

env in console

  1. I have updated build settings in Amplify console:

enter image description here

  1. I have .env.local file with my env variable added in the root folder

  2. I am running my amplify project in sandbox npx ampx sandbox and, after deployment, when I test run lambda function in AWS it doesn't print set environment variable:

2024-07-10T06:12:56.079Z e1ca42f5-9055-4e5b-9431-a5280b804363 INFO TESTZG, default!

expected result:

  • function print TESTZG, test because test is a value set in console. Printing default means thet env variable was undefined (see point 1)
 2  50  2
1 Jan 1970

Solution

 0

TL;DR - for many cases in the backend where you're using Lambda functions and you want to set a variable to different values in your sandboxes, your main branch deployment, and sub branch deployments- secrets work exactly like you want env variables to work. So what if it's a public API key- call it a secret and load the values with ease using the secrets functionality. Feel free to stop reading, that's all there is to it.

If you're determined to use environment variables (they do actually have a few uses, and you can force them to work as the OP wants, and secrets cost money), as of July 2024, the environment variables "feature" of Amplify is useful for at least two things (that I've discovered):

  1. If you're using React, you can pull them into your frontend code. This can be useful for public API keys. This is described relatively clearly in the docs you referenced. If you're using Angular (like me), this doesn't work but there are easy workarounds.
  2. On your backend's CDK configuration, you can pass variables to Lambda functions or use them to configure your backend. In a Typescript setup, it's in backend.ts. Simple applications may just have a defineBackend call here but anything beyond that will have CDK stacks.

For the backend CDK config, here are some examples. First, suppose you have an SNS Topic checkCodeTopic that you want a Lambda function called checkCode to publish to, you can pull the topic name into the Lambda's environment space:

const checkCodeTopic = new sns.Topic(logNotificationStack, "CheckCodeTopic", {
  // parameters can go here
});
// let the checkCode function publish to this topic
checkCodeTopic.grantPublish(backend.checkCode.resources.lambda);
// pass this Topic's ARN to the checkCode function's environment, so it can find it
(backend.checkCode.resources.lambda as Function).addEnvironment("CHECK_CODE_TOPIC_ARN", checkCodeTopic.topicArn);

Now in your Lambda function you can do:

const CHECK_CODE_TOPIC_ARN = process.env.CHECK_CODE_TOPIC_ARN;

That's one of the most useful uses of environment variables IMHO.

For configuring your backend, suppose you want to read an environment variable and enable deletion protection on your user pool or DynamoDB tables:

const deletionProtection = process.env.DELETION_PROTECTION;
if (deletionProtection === 'enabled') {
  console.log("ENABLING deletion protection on resources");
  // protect user pool
  const { cfnUserPool } = backend.auth.resources.cfnResources
  cfnUserPool.deletionProtection = "ACTIVE";
  ...

You use the Amplify Console to set DELETION_PROTECTION for your branch deployments. This is super helpful because you probably want deletion protection for your prod resources, you likely want it for your dev deployment so you know what happens on an upgrade before you try it on prod, and you probably do NOT want it on your sandbox. For your sandbox, you set an environment variable on your terminal like normal export DELETION_PROTECTION=enabled (for macOS/linux) then run npx ampx sandbox and it'll use that environment variable to configure your backend.

Tying this all together, if you really want to pass environment variables to your Lambdas and you don't want to do it the easy way with secrets, you just put those two concepts together in your backend.ts. For example, let's say you're using Stripe and you want to pass your public API key in a env variable called STRIPE_PUBLISHABLE_KEY into a Lambda called createStripeSession (put your secret API key in the Amplify secrets box):

// pass environment variables into the Lambda functions that need them
(backend.createStripeSession.resources.lambda as Function).addEnvironment("STRIPE_PUBLISHABLE_KEY", process.env.STRIPE_PUBLISHABLE_KEY ?? 'NO KEY DEFINED');

Again, here you'll populate those in the Amplify Console for branch deployments and your terminal for sandbox deployments. You pull it into your Lambda function the same as above:

const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY;

Honestly though, the downside is that for your sandboxes you have to define these every time you make a new terminal window. You restart VSCode for an update- export them all over again. You restart your computer, all over again. With secrets, you set them once and done and no need for this little hack in backend.ts. Of course, secrets cost $0.40 per month, so there's that.

2024-07-25
Jesse Pangburn